From 7cb6e8f59b010ed4e1fa2125b57373b22f24f99b Mon Sep 17 00:00:00 2001 From: Jeffrey Alan Scudder Date: Mon, 23 Mar 2026 13:43:17 -0700 Subject: [PATCH] =?UTF-8?q?chore(at):=20bump=20@atproto/api=20to=20^0.19.4?= =?UTF-8?q?,=20migrate=20BskyAgent=20=E2=86=92=20AtpAgent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- at/bulk-share-paintings.mjs | 4 +- at/cli.mjs | 580 +++++++++++++ at/delete-all-posts.mjs | 4 +- at/package-lock.json | 103 ++- at/package.json | 2 +- at/pds/config/pds.env.example | 6 + at/post-to-bluesky.mjs | 4 +- at/query-posts.mjs | 4 +- at/query-profile.mjs | 4 +- at/scripts/publish-lexicons.mjs | 4 +- at/share-latest-painting.mjs | 4 +- at/share-random-painting.mjs | 4 +- at/test-all.mjs | 4 +- oven/native-builder.mjs | 16 +- package-lock.json | 772 ++---------------- package.json | 13 +- reports/2026-03-23-at-protocol-audit.md | 287 +++++++ system/backend/bluesky-engagement.mjs | 6 +- system/backend/bluesky-mirror.mjs | 6 +- system/package-lock.json | 56 +- system/package.json | 2 +- .../public/aesthetic.computer/disks/blank.mjs | 59 +- system/public/aesthetic.computer/disks/os.mjs | 58 +- 23 files changed, 1178 insertions(+), 824 deletions(-) create mode 100755 at/cli.mjs create mode 100644 reports/2026-03-23-at-protocol-audit.md diff --git a/at/bulk-share-paintings.mjs b/at/bulk-share-paintings.mjs index 567478e95c..b5e12e7496 100644 --- a/at/bulk-share-paintings.mjs +++ b/at/bulk-share-paintings.mjs @@ -10,7 +10,7 @@ * node bulk-share-paintings.mjs 10 --delay 3000 */ -import { BskyAgent, RichText } from '@atproto/api' +import { AtpAgent, RichText } from '@atproto/api' import { config } from 'dotenv' config() @@ -34,7 +34,7 @@ async function login() { } console.log(`๐Ÿ” Logging in as @${BSKY_IDENTIFIER}...`) - agent = new BskyAgent({ service: BSKY_SERVICE }) + agent = new AtpAgent({ service: BSKY_SERVICE }) await agent.login({ identifier: BSKY_IDENTIFIER, password: BSKY_APP_PASSWORD diff --git a/at/cli.mjs b/at/cli.mjs new file mode 100755 index 0000000000..b9027c329c --- /dev/null +++ b/at/cli.mjs @@ -0,0 +1,580 @@ +#!/usr/bin/env node +// at/cli.mjs โ€” Unified CLI for AT Protocol tooling on Aesthetic Computer. +// Usage: node at/cli.mjs [options] +// +// Consolidates scattered AT scripts into one entry point. +// Follows the same pattern as memory/cli.mjs and papers/cli.mjs. + +import { config } from "dotenv"; +config(); // Load .env from at/ directory + +const PDS_URL = process.env.PDS_URL || "https://at.aesthetic.computer"; +const BSKY_SERVICE = + process.env.BSKY_SERVICE || "https://public.api.bsky.app"; + +// --------------------------------------------------------------------------- +// Argument parser (same style as memory/cli.mjs) +// --------------------------------------------------------------------------- + +function parseArgs(argv) { + const out = { _: [] }; + for (let i = 0; i < argv.length; i++) { + const token = argv[i]; + if (!token.startsWith("--")) { + out._.push(token); + continue; + } + const eqIdx = token.indexOf("="); + if (eqIdx !== -1) { + out[token.slice(2, eqIdx)] = token.slice(eqIdx + 1); + } else { + const next = argv[i + 1]; + if (next && !next.startsWith("--")) { + out[token.slice(2)] = next; + i++; + } else { + out[token.slice(2)] = true; + } + } + } + return out; +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function requireArg(args, position, name) { + const val = args._[position]; + if (!val) { + console.error(`Missing required argument: <${name}>`); + process.exit(1); + } + return val; +} + +async function fetchJSON(url) { + const res = await fetch(url); + if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`); + return res.json(); +} + +function adminAuth() { + const pw = process.env.PDS_ADMIN_PASSWORD; + if (!pw) { + console.error("PDS_ADMIN_PASSWORD environment variable is required."); + process.exit(1); + } + return `Basic ${Buffer.from(`admin:${pw}`).toString("base64")}`; +} + +// --------------------------------------------------------------------------- +// Commands +// --------------------------------------------------------------------------- + +async function commandHealth() { + console.log(`\nPDS Health Check โ€” ${PDS_URL}\n`); + + // HTTP health + try { + const res = await fetch(`${PDS_URL}/xrpc/_health`); + if (res.ok) { + const data = await res.json(); + console.log(` HTTP: OK (version ${data.version || "unknown"})`); + } else { + console.log(` HTTP: FAIL (${res.status})`); + } + } catch (e) { + console.log(` HTTP: FAIL (${e.message})`); + } + + // Describe server + try { + const desc = await fetchJSON( + `${PDS_URL}/xrpc/com.atproto.server.describeServer`, + ); + console.log(` DID: ${desc.did || "?"}`); + console.log( + ` Invite: ${desc.inviteCodeRequired ? "required" : "open"}`, + ); + if (desc.availableUserDomains?.length) { + console.log(` Domains: ${desc.availableUserDomains.join(", ")}`); + } + if (desc.contact?.email) { + console.log(` Contact: ${desc.contact.email}`); + } + } catch (e) { + console.log(` Server: Could not describe (${e.message})`); + } + + console.log(); +} + +async function commandResolve(args) { + const input = requireArg(args, 1, "handle-or-did"); + + let did = input; + + // Resolve handle โ†’ DID + if (!input.startsWith("did:")) { + console.log(`Resolving handle: ${input}`); + const profile = await fetchJSON( + `${BSKY_SERVICE}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(input)}`, + ); + did = profile.did; + console.log(` @${input} -> ${did}\n`); + } + + // Fetch DID document + if (did.startsWith("did:plc:")) { + const doc = await fetchJSON(`https://plc.directory/${did}`); + + console.log(`DID: ${did}`); + if (doc.alsoKnownAs?.length) { + doc.alsoKnownAs.forEach((aka) => { + const label = aka.startsWith("at://") ? `@${aka.slice(5)}` : aka; + console.log(`AKA: ${label}`); + }); + } + if (doc.service?.length) { + doc.service.forEach((svc) => { + const star = + svc.serviceEndpoint.includes("aesthetic.computer") ? " (ours)" : ""; + console.log(`Service: ${svc.type} -> ${svc.serviceEndpoint}${star}`); + }); + } + if (doc.verificationMethod?.length) { + doc.verificationMethod.forEach((vm) => { + const key = vm.publicKeyMultibase + ? vm.publicKeyMultibase.slice(0, 24) + "..." + : "?"; + console.log(`Key: ${vm.id} (${key})`); + }); + } + + if (args.json) { + console.log(`\n${JSON.stringify(doc, null, 2)}`); + } + } else if (did.startsWith("did:web:")) { + const domain = did.replace("did:web:", ""); + const doc = await fetchJSON(`https://${domain}/.well-known/did.json`); + console.log(JSON.stringify(doc, null, 2)); + } else { + console.error(`Unsupported DID method: ${did}`); + } + console.log(); +} + +async function commandProfile(args) { + const actor = requireArg(args, 1, "handle-or-did"); + const profile = await fetchJSON( + `${BSKY_SERVICE}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(actor)}`, + ); + const p = profile; + + console.log(`\n Handle: @${p.handle}`); + console.log(` DID: ${p.did}`); + console.log(` Name: ${p.displayName || "(none)"}`); + console.log(` Bio: ${p.description || "(none)"}`); + console.log(` Followers: ${p.followersCount || 0}`); + console.log(` Following: ${p.followsCount || 0}`); + console.log(` Posts: ${p.postsCount || 0}`); + if (p.avatar) console.log(` Avatar: ${p.avatar}`); + console.log(); +} + +async function commandPosts(args) { + const actor = requireArg(args, 1, "handle-or-did"); + const limit = parseInt(args.limit) || 10; + + const data = await fetchJSON( + `${BSKY_SERVICE}/xrpc/app.bsky.feed.getAuthorFeed?actor=${encodeURIComponent(actor)}&limit=${limit}`, + ); + + if (!data.feed?.length) { + console.log("(no posts)"); + return; + } + + console.log(`\n${data.feed.length} posts from @${actor}:\n`); + + data.feed.forEach((item, i) => { + const post = item.post; + const text = post.record?.text || "(no text)"; + const date = new Date(post.indexedAt).toLocaleDateString(); + const likes = post.likeCount || 0; + const replies = post.replyCount || 0; + const reposts = post.repostCount || 0; + console.log( + ` ${i + 1}. [${date}] ${text.slice(0, 80)}${text.length > 80 ? "..." : ""}`, + ); + console.log(` likes:${likes} replies:${replies} reposts:${reposts}`); + console.log(` ${post.uri}\n`); + }); +} + +async function commandPost(args) { + const text = requireArg(args, 1, "text"); + + const identifier = process.env.BSKY_IDENTIFIER; + const appPassword = process.env.BSKY_APP_PASSWORD; + const service = process.env.BSKY_SERVICE || "https://bsky.social"; + + if (!identifier || !appPassword) { + console.error( + "Set BSKY_IDENTIFIER and BSKY_APP_PASSWORD in your environment.", + ); + process.exit(1); + } + + const { AtpAgent, RichText } = await import("@atproto/api"); + const agent = new AtpAgent({ service }); + + console.log(`Logging in as @${identifier}...`); + await agent.login({ identifier, password: appPassword }); + + const rt = new RichText({ text }); + await rt.detectFacets(agent); + + const postRecord = { + text: rt.text, + facets: rt.facets, + createdAt: new Date().toISOString(), + }; + + // Attach image if provided + if (args.image) { + const { readFileSync } = await import("fs"); + const imageData = readFileSync(args.image); + const { data } = await agent.uploadBlob(imageData, { + encoding: "image/png", + }); + postRecord.embed = { + $type: "app.bsky.embed.images", + images: [ + { image: data.blob, alt: args.alt || "Image from Aesthetic Computer" }, + ], + }; + console.log(`Uploaded image: ${args.image}`); + } + + const response = await agent.post(postRecord); + const rkey = response.uri.split("/").pop(); + + console.log(`Posted: https://bsky.app/profile/${identifier}/post/${rkey}`); +} + +async function commandRecords(args) { + const repo = requireArg(args, 1, "did"); + const collection = requireArg(args, 2, "collection"); + const limit = parseInt(args.limit) || 25; + + const data = await fetchJSON( + `${PDS_URL}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(repo)}&collection=${encodeURIComponent(collection)}&limit=${limit}`, + ); + + if (!data.records?.length) { + console.log(`(no records in ${collection})`); + return; + } + + console.log(`\n${data.records.length} records in ${collection}:\n`); + + data.records.forEach((rec, i) => { + const rkey = rec.uri.split("/").pop(); + const val = rec.value; + // Show a compact summary depending on type + const when = val.when || val.createdAt || ""; + const label = + val.slug || val.code || val.mood || val.headline || val.text || ""; + console.log( + ` ${i + 1}. ${rkey} ${label.slice(0, 60)} ${when ? `(${when.slice(0, 10)})` : ""}`, + ); + }); + console.log(); +} + +async function commandLexicons() { + const { readdirSync, readFileSync } = await import("fs"); + const { join, dirname } = await import("path"); + const { fileURLToPath } = await import("url"); + + const __dirname = dirname(fileURLToPath(import.meta.url)); + const lexDir = join(__dirname, "lexicons", "computer", "aesthetic"); + + let files; + try { + files = readdirSync(lexDir).filter((f) => f.endsWith(".json")); + } catch { + console.error(`Lexicon directory not found: ${lexDir}`); + return; + } + + console.log(`\nAesthetic Computer Lexicons (${files.length}):\n`); + + for (const file of files) { + const lex = JSON.parse(readFileSync(join(lexDir, file), "utf8")); + const main = lex.defs?.main; + const desc = main?.description || ""; + const required = main?.record?.required || []; + const props = Object.keys(main?.record?.properties || {}); + + console.log(` ${lex.id}`); + console.log(` ${desc}`); + console.log(` required: ${required.join(", ") || "(none)"}`); + console.log(` fields: ${props.join(", ")}`); + console.log(); + } +} + +async function commandInvite() { + const auth = adminAuth(); + + const res = await fetch( + `${PDS_URL}/xrpc/com.atproto.server.createInviteCode`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: auth, + }, + body: JSON.stringify({ useCount: 1 }), + }, + ); + + if (!res.ok) { + const text = await res.text(); + console.error(`Failed to create invite: ${res.status} ${text}`); + process.exit(1); + } + + const data = await res.json(); + console.log(`Invite code: ${data.code}`); +} + +async function commandAccounts(args) { + const limit = parseInt(args.limit) || 50; + + // listRepos is a public endpoint + const res = await fetch( + `${PDS_URL}/xrpc/com.atproto.sync.listRepos?limit=${limit}`, + ); + + if (!res.ok) { + console.error(`Failed to list accounts: ${res.status}`); + process.exit(1); + } + + const data = await res.json(); + printRepos(data); +} + +function printRepos(data) { + const repos = data.repos || []; + console.log(`\n${repos.length} accounts on PDS:\n`); + repos.forEach((repo, i) => { + const active = repo.active !== false ? "" : " (inactive)"; + console.log( + ` ${String(i + 1).padStart(3)}. ${repo.did}${active}`, + ); + }); + console.log(); +} + +async function commandAccountCheck(args) { + const input = requireArg(args, 1, "handle-or-did"); + + // Resolve to DID if needed + let did = input; + if (!input.startsWith("did:")) { + const profile = await fetchJSON( + `${BSKY_SERVICE}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(input)}`, + ); + did = profile.did; + } + + console.log(`\nAccount check for: ${did}\n`); + + // Check DID document + try { + const doc = await fetchJSON(`https://plc.directory/${did}`); + const pds = doc.service?.find( + (s) => s.type === "AtprotoPersonalDataServer", + ); + const handle = doc.alsoKnownAs + ?.find((a) => a.startsWith("at://")) + ?.slice(5); + + console.log(` Handle: @${handle || "?"}`); + console.log(` PDS: ${pds?.serviceEndpoint || "?"}`); + + if (pds?.serviceEndpoint?.includes("aesthetic.computer")) { + console.log(` Ours: yes`); + } + } catch (e) { + console.log(` DID doc: failed (${e.message})`); + } + + // List collections on our PDS + const collections = [ + "computer.aesthetic.painting", + "computer.aesthetic.mood", + "computer.aesthetic.piece", + "computer.aesthetic.kidlisp", + "computer.aesthetic.tape", + "computer.aesthetic.news", + ]; + + console.log(`\n Records on ${PDS_URL}:`); + for (const col of collections) { + try { + const data = await fetchJSON( + `${PDS_URL}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(col)}&limit=1`, + ); + const count = data.records?.length + ? `${data.records.length}+ records` + : "0 records"; + console.log(` ${col.replace("computer.aesthetic.", "")}: ${count}`); + } catch { + console.log( + ` ${col.replace("computer.aesthetic.", "")}: (error or not found)`, + ); + } + } + console.log(); +} + +async function commandSyncStatus() { + console.log(`\nSync Status โ€” ${PDS_URL}\n`); + + // Get the art account DID (guest) + const artDid = "did:plc:tliuubv7lyv2uiknsjbf4ppw"; + + const collections = [ + "computer.aesthetic.painting", + "computer.aesthetic.mood", + "computer.aesthetic.piece", + "computer.aesthetic.kidlisp", + "computer.aesthetic.tape", + "computer.aesthetic.news", + ]; + + // Check record counts on art account as a quick indicator + console.log(` Art account (${artDid}):`); + for (const col of collections) { + try { + const data = await fetchJSON( + `${PDS_URL}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(artDid)}&collection=${encodeURIComponent(col)}&limit=100`, + ); + const name = col.replace("computer.aesthetic.", ""); + const count = data.records?.length || 0; + const cursor = data.cursor ? " (more available)" : ""; + console.log(` ${name.padEnd(12)} ${count} records${cursor}`); + } catch { + const name = col.replace("computer.aesthetic.", ""); + console.log(` ${name.padEnd(12)} (error)`); + } + } + + // List all repos to get user count + try { + const repos = await fetchJSON( + `${PDS_URL}/xrpc/com.atproto.sync.listRepos?limit=200`, + ); + const count = repos.repos?.length || 0; + console.log(`\n Total accounts: ${count}`); + } catch { + console.log(`\n Total accounts: (could not fetch)`); + } + + console.log(); +} + +// --------------------------------------------------------------------------- +// Help +// --------------------------------------------------------------------------- + +function printHelp() { + console.log(`ac-at โ€” AT Protocol CLI for Aesthetic Computer + +Usage: ac-at [options] + +Query & Inspect: + health PDS health check + resolve [--json] Resolve DID document + profile Query profile + posts [--limit=N] Query posts + records [--limit=N] List records + lexicons Show AC custom lexicon schemas + +Publish: + post [--image=path] [--alt=text] Post to Bluesky + +Admin (requires PDS_ADMIN_PASSWORD): + invite Generate PDS invite code + accounts [--limit=N] List PDS accounts + account:check Inspect account & record counts + sync:status Record counts across collections + +Environment: + PDS_URL PDS endpoint (default: https://at.aesthetic.computer) + PDS_ADMIN_PASSWORD Admin password for PDS operations + BSKY_IDENTIFIER Bluesky handle for posting + BSKY_APP_PASSWORD Bluesky app password for posting + BSKY_SERVICE Bluesky API (default: https://public.api.bsky.app) + +Examples: + ac-at health + ac-at resolve aesthetic.computer + ac-at profile jeffrey.at.aesthetic.computer + ac-at posts aesthetic.computer --limit=5 + ac-at records did:plc:k3k3wknzkcnekbnyde4dbatz computer.aesthetic.painting + ac-at post "Hello from AC!" --image=painting.png + ac-at invite + ac-at account:check jeffrey.at.aesthetic.computer + ac-at sync:status +`); +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + +const COMMANDS = { + health: commandHealth, + resolve: commandResolve, + profile: commandProfile, + posts: commandPosts, + post: commandPost, + records: commandRecords, + lexicons: commandLexicons, + invite: commandInvite, + accounts: commandAccounts, + "account:check": commandAccountCheck, + "sync:status": commandSyncStatus, +}; + +async function main() { + const args = parseArgs(process.argv.slice(2)); + const command = args._[0] || "help"; + + if (command === "help" || command === "--help" || command === "-h") { + printHelp(); + return; + } + + const handler = COMMANDS[command]; + if (!handler) { + console.error(`Unknown command: ${command}\n`); + printHelp(); + process.exitCode = 1; + return; + } + + await handler(args); +} + +main().catch((err) => { + console.error(`ac-at: ${err.message}`); + process.exit(1); +}); diff --git a/at/delete-all-posts.mjs b/at/delete-all-posts.mjs index eed82af596..cc5e406556 100644 --- a/at/delete-all-posts.mjs +++ b/at/delete-all-posts.mjs @@ -10,7 +10,7 @@ * node delete-all-posts.mjs --confirm */ -import { BskyAgent } from '@atproto/api' +import { AtpAgent } from '@atproto/api' import { config } from 'dotenv' config() @@ -31,7 +31,7 @@ async function deleteAllPosts() { console.log(`\n๐Ÿ—‘๏ธ Delete All Posts from @${BSKY_IDENTIFIER}\n`) console.log('โ•'.repeat(50) + '\n') - const agent = new BskyAgent({ service: BSKY_SERVICE }) + const agent = new AtpAgent({ service: BSKY_SERVICE }) try { // Login diff --git a/at/package-lock.json b/at/package-lock.json index 0453ada1c8..9625c1a55f 100644 --- a/at/package-lock.json +++ b/at/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@atproto/api": "^0.17.0", + "@atproto/api": "^0.19.4", "@atproto/identity": "^0.4.3", "@atproto/lexicon": "^0.4.2", "@atproto/syntax": "^0.3.1", @@ -22,15 +22,15 @@ } }, "node_modules/@atproto/api": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.17.0.tgz", - "integrity": "sha512-FNS9SW7/3kslAnJH7F4fO9/jPjXzC0NMD6u9NjJ/h4EnaIEpWHZQPkmD9Q2hvAwD6+Uo2boYZEPKkOa55Lr5Dg==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.19.4.tgz", + "integrity": "sha512-fYNM62vdXxer0h8a9Jzl4/ag9uFIe0nTO+LkC6KTlx1yUDigrAoQMMbllIiCWj62GhUMxAkHabk/BZjjVAfKng==", "license": "MIT", "dependencies": { - "@atproto/common-web": "^0.4.3", - "@atproto/lexicon": "^0.5.1", - "@atproto/syntax": "^0.4.1", - "@atproto/xrpc": "^0.7.5", + "@atproto/common-web": "^0.4.18", + "@atproto/lexicon": "^0.6.2", + "@atproto/syntax": "^0.5.1", + "@atproto/xrpc": "^0.7.7", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", @@ -38,46 +38,58 @@ } }, "node_modules/@atproto/api/node_modules/@atproto/lexicon": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", - "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.2.tgz", + "integrity": "sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==", "license": "MIT", "dependencies": { - "@atproto/common-web": "^0.4.3", - "@atproto/syntax": "^0.4.1", + "@atproto/common-web": "^0.4.18", + "@atproto/syntax": "^0.5.0", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "node_modules/@atproto/api/node_modules/@atproto/syntax": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", - "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", - "license": "MIT" + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } }, "node_modules/@atproto/api/node_modules/@atproto/xrpc": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", - "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.7.tgz", + "integrity": "sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==", "license": "MIT", "dependencies": { - "@atproto/lexicon": "^0.5.1", + "@atproto/lexicon": "^0.6.0", "zod": "^3.23.8" } }, "node_modules/@atproto/common-web": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", - "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.19.tgz", + "integrity": "sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw==", "license": "MIT", "dependencies": { - "graphemer": "^1.4.0", - "multiformats": "^9.9.0", - "uint8arrays": "3.0.0", + "@atproto/lex-data": "^0.0.14", + "@atproto/lex-json": "^0.0.14", + "@atproto/syntax": "^0.5.1", "zod": "^3.23.8" } }, + "node_modules/@atproto/common-web/node_modules/@atproto/syntax": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } + }, "node_modules/@atproto/crypto": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.4.tgz", @@ -105,6 +117,28 @@ "node": ">=18.7.0" } }, + "node_modules/@atproto/lex-data": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.14.tgz", + "integrity": "sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ==", + "license": "MIT", + "dependencies": { + "multiformats": "^9.9.0", + "tslib": "^2.8.1", + "uint8arrays": "3.0.0", + "unicode-segmenter": "^0.14.0" + } + }, + "node_modules/@atproto/lex-json": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.14.tgz", + "integrity": "sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww==", + "license": "MIT", + "dependencies": { + "@atproto/lex-data": "^0.0.14", + "tslib": "^2.8.1" + } + }, "node_modules/@atproto/lexicon": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.14.tgz", @@ -641,12 +675,6 @@ "url": "https://dotenvx.com" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "license": "MIT" - }, "node_modules/iso-datestring-validator": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", @@ -726,8 +754,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/uint8arrays": { "version": "3.0.0", @@ -745,6 +772,12 @@ "dev": true, "license": "MIT" }, + "node_modules/unicode-segmenter": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", + "license": "MIT" + }, "node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", diff --git a/at/package.json b/at/package.json index b624ba9a56..972b00e6de 100644 --- a/at/package.json +++ b/at/package.json @@ -14,7 +14,7 @@ "test": "node test-all.mjs" }, "dependencies": { - "@atproto/api": "^0.17.0", + "@atproto/api": "^0.19.4", "@atproto/identity": "^0.4.3", "@atproto/lexicon": "^0.4.2", "@atproto/syntax": "^0.3.1", diff --git a/at/pds/config/pds.env.example b/at/pds/config/pds.env.example index 60b5dfdfb5..8862740b6a 100644 --- a/at/pds/config/pds.env.example +++ b/at/pds/config/pds.env.example @@ -87,6 +87,12 @@ PDS_BLOBSTORE_S3_FORCE_PATH_STYLE=true # From address for emails PDS_EMAIL_FROM_ADDRESS=noreply@aesthetic.computer +# ============================================================================= +# OPTIONAL: Contact Information (shown in describeServer) +# ============================================================================= + +PDS_CONTACT_EMAIL_ADDRESS=mail@aesthetic.computer + # ============================================================================= # OPTIONAL: Service Configuration # ============================================================================= diff --git a/at/post-to-bluesky.mjs b/at/post-to-bluesky.mjs index 1db07bf22c..3baedc39a2 100644 --- a/at/post-to-bluesky.mjs +++ b/at/post-to-bluesky.mjs @@ -11,7 +11,7 @@ * node post-to-bluesky.mjs "New painting!" --image painting.png */ -import { BskyAgent } from '@atproto/api' +import { AtpAgent } from '@atproto/api' import { config } from 'dotenv' import { readFileSync } from 'fs' @@ -34,7 +34,7 @@ async function postToBluesky(text, options = {}) { console.log(`\n๐Ÿ“ค Posting to Bluesky as @${BSKY_IDENTIFIER}`) console.log(`๐Ÿ“ก Using service: ${BSKY_SERVICE}\n`) - const agent = new BskyAgent({ service: BSKY_SERVICE }) + const agent = new AtpAgent({ service: BSKY_SERVICE }) try { // Login diff --git a/at/query-posts.mjs b/at/query-posts.mjs index 6383b1165a..3fa5c5c91b 100644 --- a/at/query-posts.mjs +++ b/at/query-posts.mjs @@ -10,7 +10,7 @@ * node query-posts.mjs aesthetic.computer --limit 20 */ -import { BskyAgent } from '@atproto/api' +import { AtpAgent } from '@atproto/api' import { config } from 'dotenv' config() @@ -22,7 +22,7 @@ async function queryPosts(actor, limit = 10) { console.log(`๐Ÿ“ก Using service: ${BSKY_SERVICE}`) console.log(`๐Ÿ“Š Limit: ${limit}\n`) - const agent = new BskyAgent({ service: BSKY_SERVICE }) + const agent = new AtpAgent({ service: BSKY_SERVICE }) try { const feed = await agent.getAuthorFeed({ actor, limit }) diff --git a/at/query-profile.mjs b/at/query-profile.mjs index 9b2ae7b5db..a937333795 100644 --- a/at/query-profile.mjs +++ b/at/query-profile.mjs @@ -17,7 +17,7 @@ * node query-profile.mjs did:plc:z72i7hdynmk6r22z27h6tvur */ -import { BskyAgent } from '@atproto/api' +import { AtpAgent } from '@atproto/api' import { config } from 'dotenv' config() // Load .env if it exists @@ -28,7 +28,7 @@ async function queryProfile(actor) { console.log(`\n๐Ÿ” Querying profile for: ${actor}`) console.log(`๐Ÿ“ก Using service: ${BSKY_SERVICE}\n`) - const agent = new BskyAgent({ service: BSKY_SERVICE }) + const agent = new AtpAgent({ service: BSKY_SERVICE }) try { // Use unauthenticated API endpoint diff --git a/at/scripts/publish-lexicons.mjs b/at/scripts/publish-lexicons.mjs index c9ddd0e31c..c874119685 100644 --- a/at/scripts/publish-lexicons.mjs +++ b/at/scripts/publish-lexicons.mjs @@ -26,7 +26,7 @@ * node publish-lexicons.mjs --verify-dns # Verify DNS is configured correctly */ -import { BskyAgent } from '@atproto/api'; +import { AtpAgent } from '@atproto/api'; import { readdir, readFile } from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -348,7 +348,7 @@ async function main() { log(`Identifier: ${identifier}`, 'dim'); log(`Note: Publishing lexicons to main account (aesthetic.computer)`, 'yellow'); - const agent = new BskyAgent({ service: pdsUrl }); + const agent = new AtpAgent({ service: pdsUrl }); try { await agent.login({ diff --git a/at/share-latest-painting.mjs b/at/share-latest-painting.mjs index 20a5dd8e91..2e8eb03a35 100644 --- a/at/share-latest-painting.mjs +++ b/at/share-latest-painting.mjs @@ -12,7 +12,7 @@ * node share-latest-painting.mjs @handle --message "Custom message" */ -import { BskyAgent, RichText } from '@atproto/api' +import { AtpAgent, RichText } from '@atproto/api' import { config } from 'dotenv' import { writeFileSync } from 'fs' @@ -115,7 +115,7 @@ async function postToBluesky(paintingData, imageBuffer, message) { console.log(`๐Ÿ“ค Posting to Bluesky as @${BSKY_IDENTIFIER}`) console.log(`๐Ÿ“ก Using service: ${BSKY_SERVICE}\n`) - const agent = new BskyAgent({ service: BSKY_SERVICE }) + const agent = new AtpAgent({ service: BSKY_SERVICE }) try { // Login diff --git a/at/share-random-painting.mjs b/at/share-random-painting.mjs index 2c5127ba27..1c3bb4f4d3 100644 --- a/at/share-random-painting.mjs +++ b/at/share-random-painting.mjs @@ -11,7 +11,7 @@ * node share-random-painting.mjs --preview */ -import { BskyAgent, RichText } from '@atproto/api' +import { AtpAgent, RichText } from '@atproto/api' import { config } from 'dotenv' import { writeFileSync } from 'fs' import sharp from 'sharp' @@ -191,7 +191,7 @@ async function postToBluesky(paintingData, imageBuffer) { console.log(`๐Ÿ“ค Posting to Bluesky as @${BSKY_IDENTIFIER}\n`) - const agent = new BskyAgent({ service: BSKY_SERVICE }) + const agent = new AtpAgent({ service: BSKY_SERVICE }) try { // Login diff --git a/at/test-all.mjs b/at/test-all.mjs index 01fd0dee1f..60531ddd53 100644 --- a/at/test-all.mjs +++ b/at/test-all.mjs @@ -9,7 +9,7 @@ console.log('\n๐Ÿงช Testing ATProto Tools\n') console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n') -import { BskyAgent } from '@atproto/api' +import { AtpAgent } from '@atproto/api' const testHandle = 'aesthetic.computer' const service = 'https://public.api.bsky.app' @@ -43,7 +43,7 @@ try { console.log(' โญ๏ธ Skipped (no credentials in .env)') console.log(' ๐Ÿ’ก To test: Copy .env.example to .env and add credentials\n') } else { - const agent = new BskyAgent({ service: 'https://bsky.social' }) + const agent = new AtpAgent({ service: 'https://bsky.social' }) await agent.login({ identifier: process.env.BSKY_IDENTIFIER, password: process.env.BSKY_APP_PASSWORD diff --git a/oven/native-builder.mjs b/oven/native-builder.mjs index 38833f1178..6bf412ec23 100644 --- a/oven/native-builder.mjs +++ b/oven/native-builder.mjs @@ -58,10 +58,18 @@ function addLogLine(job, stream, line) { job.logs.splice(0, job.logs.length - MAX_LOG_LINES); job.updatedAt = nowISO(); - // Throttled progress broadcast (every 2s max) - if (progressCallback && Date.now() - lastProgressBroadcast > 2000) { - lastProgressBroadcast = Date.now(); - progressCallback(makeSnapshot(job)); + // Broadcast every log line with the last few lines attached + if (progressCallback) { + const now = Date.now(); + // Full snapshot every 2s, lightweight log-only message in between + if (now - lastProgressBroadcast > 2000) { + lastProgressBroadcast = now; + const snap = makeSnapshot(job); + snap.recentLines = job.logs.slice(-8).map(l => l.line); + progressCallback(snap); + } else { + progressCallback({ id: job.id, line: clean, stage: job.stage, percent: job.percent }); + } } // Parse progress hints from build-and-flash.sh + upload-release.sh output diff --git a/package-lock.json b/package-lock.json index ecaad22f7b..7fcc5ea0bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@atproto/api": "^0.17.2", + "@atproto/api": "^0.19.4", "@aws-sdk/client-s3": "^3.934.0", "@babel/core": "^7.28.5", "@babel/preset-react": "^7.28.5", @@ -23,7 +23,6 @@ "gbasm": "^0.0.21", "gif-encoder-2": "^1.0.5", "ink": "^6.5.1", - "instagram-private-api": "^1.46.1", "jszip": "^3.10.1", "mongodb": "^6.20.0", "node-datachannel": "^0.30.0", @@ -93,15 +92,15 @@ } }, "node_modules/@atproto/api": { - "version": "0.17.7", - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.17.7.tgz", - "integrity": "sha512-V+OJBZq9chcrD21xk1bUa6oc5DSKfQj5DmUPf5rmZncqL1w9ZEbS38H5cMyqqdhfgo2LWeDRdZHD0rvNyJsIaw==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.19.4.tgz", + "integrity": "sha512-fYNM62vdXxer0h8a9Jzl4/ag9uFIe0nTO+LkC6KTlx1yUDigrAoQMMbllIiCWj62GhUMxAkHabk/BZjjVAfKng==", "license": "MIT", "dependencies": { - "@atproto/common-web": "^0.4.3", - "@atproto/lexicon": "^0.5.1", - "@atproto/syntax": "^0.4.1", - "@atproto/xrpc": "^0.7.5", + "@atproto/common-web": "^0.4.18", + "@atproto/lexicon": "^0.6.2", + "@atproto/syntax": "^0.5.1", + "@atproto/xrpc": "^0.7.7", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", @@ -109,43 +108,68 @@ } }, "node_modules/@atproto/common-web": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", - "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.19.tgz", + "integrity": "sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw==", + "license": "MIT", + "dependencies": { + "@atproto/lex-data": "^0.0.14", + "@atproto/lex-json": "^0.0.14", + "@atproto/syntax": "^0.5.1", + "zod": "^3.23.8" + } + }, + "node_modules/@atproto/lex-data": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.14.tgz", + "integrity": "sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ==", "license": "MIT", "dependencies": { - "graphemer": "^1.4.0", "multiformats": "^9.9.0", + "tslib": "^2.8.1", "uint8arrays": "3.0.0", - "zod": "^3.23.8" + "unicode-segmenter": "^0.14.0" + } + }, + "node_modules/@atproto/lex-json": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.14.tgz", + "integrity": "sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww==", + "license": "MIT", + "dependencies": { + "@atproto/lex-data": "^0.0.14", + "tslib": "^2.8.1" } }, "node_modules/@atproto/lexicon": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", - "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.2.tgz", + "integrity": "sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==", "license": "MIT", "dependencies": { - "@atproto/common-web": "^0.4.3", - "@atproto/syntax": "^0.4.1", + "@atproto/common-web": "^0.4.18", + "@atproto/syntax": "^0.5.0", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "node_modules/@atproto/syntax": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", - "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", - "license": "MIT" + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.8.1" + } }, "node_modules/@atproto/xrpc": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", - "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.7.tgz", + "integrity": "sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==", "license": "MIT", "dependencies": { - "@atproto/lexicon": "^0.5.1", + "@atproto/lexicon": "^0.6.0", "zod": "^3.23.8" } }, @@ -3096,12 +3120,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@lifeomic/attempt": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@lifeomic/attempt/-/attempt-3.1.0.tgz", - "integrity": "sha512-QZqem4QuAnAyzfz+Gj5/+SLxqwCAw2qmt7732ZXodr6VDWGeYLG6w1i/vYLa55JQM9wRuBKLmXmiZ2P0LtE5rw==", - "license": "MIT" - }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -6294,12 +6312,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/bluebird": { - "version": "3.5.42", - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.42.tgz", - "integrity": "sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==", - "license": "MIT" - }, "node_modules/@types/bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", @@ -6331,18 +6343,6 @@ "@types/responselike": "^1.0.0" } }, - "node_modules/@types/caseless": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", - "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", - "license": "MIT" - }, - "node_modules/@types/chance": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@types/chance/-/chance-1.1.7.tgz", - "integrity": "sha512-40you9610GTQPJyvjMBgmj9wiDO6qXhbfjizNYod/fmvLSfUUxURAJMTD8tjmbcZSsyYE5iEUox61AAcCjW/wQ==", - "license": "MIT" - }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -6472,65 +6472,6 @@ "xmlbuilder": ">=11.0.1" } }, - "node_modules/@types/request": { - "version": "2.48.13", - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz", - "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==", - "license": "MIT", - "dependencies": { - "@types/caseless": "*", - "@types/node": "*", - "@types/tough-cookie": "*", - "form-data": "^2.5.5" - } - }, - "node_modules/@types/request-promise": { - "version": "4.1.51", - "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.51.tgz", - "integrity": "sha512-qVcP9Fuzh9oaAh8oPxiSoWMFGnWKkJDknnij66vi09Yiy62bsSDqtd+fG5kIM9wLLgZsRP3Y6acqj9O/v2ZtRw==", - "license": "MIT", - "dependencies": { - "@types/bluebird": "*", - "@types/request": "*" - } - }, - "node_modules/@types/request/node_modules/form-data": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", - "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.35", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@types/request/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -6571,12 +6512,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "license": "MIT" - }, "node_modules/@types/verror": { "version": "1.10.11", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", @@ -6746,6 +6681,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -7018,20 +6954,13 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=0.8" } @@ -7136,21 +7065,6 @@ "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", "license": "MIT" }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "license": "MIT" - }, "node_modules/axe-core": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", @@ -7320,15 +7234,6 @@ "node": ">=10.0.0" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/bignumber.js": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", @@ -7428,12 +7333,6 @@ "node": ">= 0.8.0" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "license": "MIT" - }, "node_modules/bn.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", @@ -7925,12 +7824,6 @@ "dev": true, "license": "MIT" }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "license": "Apache-2.0" - }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -7943,12 +7836,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chance": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.13.tgz", - "integrity": "sha512-V6lQCljcLznE7tUYUM9EOAnnKXbctE6j/rdQkYOHIWbfGQbrzTsAXNW9CdU5XCo4ArXQCj/rb6HgxPlmGJcaUg==", - "license": "MIT" - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -8087,12 +7974,6 @@ "dev": true, "license": "MIT" }, - "node_modules/class-transformer": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.3.1.tgz", - "integrity": "sha512-cKFwohpJbuMovS8xVLmn8N2AUbAuc8pVo4zEfsUVo8qgECOogns1WVk/FkOZoxhOPTyTYFckuoH+13FO+MQ8GA==", - "license": "MIT" - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -8761,18 +8642,6 @@ "uniq": "^1.0.0" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -9162,16 +9031,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -9786,12 +9645,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -9844,6 +9697,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-fifo": { @@ -10029,15 +9883,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/form-data": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", @@ -10367,15 +10212,6 @@ "node": ">= 14" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/gif-encoder-2": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/gif-encoder-2/-/gif-encoder-2-1.0.5.tgz", @@ -10522,35 +10358,6 @@ "node": ">=14" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "license": "MIT" - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -10857,21 +10664,6 @@ "node": ">=8" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/http2-wrapper": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", @@ -10968,18 +10760,6 @@ "dev": true, "license": "ISC" }, - "node_modules/image-size": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", - "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==", - "license": "MIT", - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/image-ssim": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/image-ssim/-/image-ssim-0.2.0.tgz", @@ -11272,64 +11052,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/instagram-private-api": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/instagram-private-api/-/instagram-private-api-1.46.1.tgz", - "integrity": "sha512-fq0q6UfhpikKZ5Kw8HNwS6YpsNghE9I/uc8AM9Do9nsQ+3H1u0jLz+0t/FcGkGTjZz5VGvU8s2VbWj9wxchwYg==", - "license": "MIT", - "dependencies": { - "@lifeomic/attempt": "^3.0.0", - "@types/chance": "^1.0.2", - "@types/request-promise": "^4.1.43", - "bluebird": "^3.7.1", - "chance": "^1.0.18", - "class-transformer": "^0.3.1", - "debug": "^4.1.1", - "image-size": "^0.7.3", - "json-bigint": "^1.0.0", - "lodash": "^4.17.20", - "luxon": "^1.12.1", - "reflect-metadata": "^0.1.13", - "request": "^2.88.0", - "request-promise": "^4.2.4", - "rxjs": "^6.5.2", - "snakecase-keys": "^3.1.0", - "tough-cookie": "^2.5.0", - "ts-custom-error": "^2.2.2", - "ts-xor": "^1.0.6", - "url-regex-safe": "^3.0.0", - "utility-types": "^3.10.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "re2": "^1.17.2" - }, - "peerDependenciesMeta": { - "re2": { - "optional": true - } - } - }, - "node_modules/instagram-private-api/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/instagram-private-api/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, "node_modules/intl-messageformat": { "version": "10.7.18", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", @@ -11360,15 +11082,6 @@ "node": ">= 12" } }, - "node_modules/ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -11566,12 +11279,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -11638,12 +11345,6 @@ "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "license": "MIT" - }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -11734,12 +11435,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "license": "MIT" - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -11752,15 +11447,6 @@ "node": ">=6" } }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -11775,16 +11461,11 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, "license": "MIT" }, "node_modules/json-stringify-safe": { @@ -11815,50 +11496,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/jsprim/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "license": "MIT" - }, - "node_modules/jsprim/node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/jsprim/node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -12207,15 +11844,6 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, - "node_modules/luxon": { - "version": "1.28.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", - "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -12331,18 +11959,6 @@ "node": ">=8" } }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/marky": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", @@ -13056,15 +12672,6 @@ "node": ">= 0.10.0" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -13578,12 +13185,6 @@ "dev": true, "license": "MIT" }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "license": "MIT" - }, "node_modules/pg-int8": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", @@ -13985,18 +13586,6 @@ "dev": true, "license": "MIT" }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -14427,107 +14016,12 @@ "node": ">=8.10.0" } }, - "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "license": "Apache-2.0" - }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "license": "MIT" }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request-promise": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", - "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", - "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", - "license": "ISC", - "dependencies": { - "bluebird": "^3.5.0", - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "license": "ISC", - "dependencies": { - "lodash": "^4.17.19" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "request": "^2.34" - } - }, - "node_modules/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", - "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14788,6 +14282,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "devOptional": true, "license": "MIT" }, "node_modules/sanitize-filename": { @@ -15254,19 +14749,6 @@ "npm": ">= 3.0.0" } }, - "node_modules/snakecase-keys": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.1.tgz", - "integrity": "sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==", - "license": "MIT", - "dependencies": { - "map-obj": "^4.1.0", - "to-snake-case": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/socks": { "version": "2.8.7", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", @@ -15361,31 +14843,6 @@ "license": "BSD-3-Clause", "optional": true }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ssri": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", @@ -15443,15 +14900,6 @@ "node": ">= 6" } }, - "node_modules/stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", - "license": "ISC", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/streamx": { "version": "2.22.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", @@ -16058,12 +15506,6 @@ ], "license": "MIT" }, - "node_modules/to-no-case": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==", - "license": "MIT" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -16077,24 +15519,6 @@ "node": ">=8.0" } }, - "node_modules/to-snake-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", - "integrity": "sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==", - "license": "MIT", - "dependencies": { - "to-space-case": "^1.0.0" - } - }, - "node_modules/to-space-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", - "license": "MIT", - "dependencies": { - "to-no-case": "^1.0.0" - } - }, "node_modules/touch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", @@ -16105,19 +15529,6 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/tr46": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", @@ -16160,22 +15571,6 @@ "utf8-byte-length": "^1.0.1" } }, - "node_modules/ts-custom-error": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-2.2.2.tgz", - "integrity": "sha512-I0FEdfdatDjeigRqh1JFj67bcIKyRNm12UVGheBjs2pXgyELg2xeiQLVaWu1pVmNGXZVnz/fvycSU41moBIpOg==", - "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", - "license": "WTFPL", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/ts-xor": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-xor/-/ts-xor-1.3.0.tgz", - "integrity": "sha512-RLXVjliCzc1gfKQFLRpfeD0rrWmjnSTgj7+RFhoq3KRkUYa8LE/TIidYOzM5h+IdFBDSjjSgk9Lto9sdMfDFEA==", - "license": "MIT" - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -16194,12 +15589,6 @@ "node": "*" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "license": "Unlicense" - }, "node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", @@ -16289,6 +15678,12 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, + "node_modules/unicode-segmenter": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", + "license": "MIT" + }, "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -16378,6 +15773,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -16390,27 +15786,6 @@ "dev": true, "license": "MIT" }, - "node_modules/url-regex-safe": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-regex-safe/-/url-regex-safe-3.0.0.tgz", - "integrity": "sha512-+2U40NrcmtWFVjuxXVt9bGRw6c7/MgkGKN9xIfPrT/2RX0LTkkae6CCEDp93xqUN0UKm/rr821QnHd2dHQmN3A==", - "license": "MIT", - "dependencies": { - "ip-regex": "4.3.0", - "tlds": "^1.228.0" - }, - "engines": { - "node": ">= 10.12.0" - }, - "peerDependencies": { - "re2": "^1.17.2" - }, - "peerDependenciesMeta": { - "re2": { - "optional": true - } - } - }, "node_modules/utf8-byte-length": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", @@ -16424,25 +15799,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, - "node_modules/utility-types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "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.", - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/verror": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", diff --git a/package.json b/package.json index 97d4f86ebc..d95e0b3055 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,17 @@ "agent-memory:sync-codex": "node memory/codex-sync.mjs", "agent-memory:install-hooks": "node memory/install-hooks.mjs", "agent-memory:flush": "node memory/cli.mjs flush-remote", + "at": "node at/cli.mjs", + "at:health": "node at/cli.mjs health", + "at:resolve": "node at/cli.mjs resolve", + "at:profile": "node at/cli.mjs profile", + "at:posts": "node at/cli.mjs posts", + "at:post": "node at/cli.mjs post", + "at:records": "node at/cli.mjs records", + "at:lexicons": "node at/cli.mjs lexicons", + "at:invite": "node at/cli.mjs invite", + "at:accounts": "node at/cli.mjs accounts", + "at:sync": "node at/cli.mjs sync:status", "profile:secret:rotate": "node utilities/rotate-profile-stream-secret.mjs", "ac": "./aesthetic", "admin:udp": "ssh root@157.245.134.225", @@ -125,7 +136,7 @@ "terminal-kit": "^3.1.2" }, "dependencies": { - "@atproto/api": "^0.17.2", + "@atproto/api": "^0.19.4", "@aws-sdk/client-s3": "^3.934.0", "@babel/core": "^7.28.5", "@babel/preset-react": "^7.28.5", diff --git a/reports/2026-03-23-at-protocol-audit.md b/reports/2026-03-23-at-protocol-audit.md new file mode 100644 index 0000000000..c271da2277 --- /dev/null +++ b/reports/2026-03-23-at-protocol-audit.md @@ -0,0 +1,287 @@ +# AT Protocol Integration Audit + +**Date:** 2026-03-23 +**Scope:** at.aesthetic.computer PDS, ATProto sync, tooling, lexicons, CLI + +--- + +## 1. Current State Overview + +Aesthetic Computer runs a **self-hosted PDS** (Personal Data Server) at `at.aesthetic.computer` on a DigitalOcean droplet. The integration is production-grade with: + +- 6 custom lexicons under `computer.aesthetic.*` +- Bidirectional sync between MongoDB and PDS for paintings, moods, tapes, kidlisp, pieces, news +- User account provisioning (handle format: `{handle}.at.aesthetic.computer`) +- Guest/anonymous content via `art.at.aesthetic.computer` +- 31+ admin scripts, 28+ sync/verification scripts +- User profile pages at `{handle}.at.aesthetic.computer` + +**PDS Stack:** DigitalOcean droplet (1 CPU, 1GB RAM, 25GB SSD โ€” $6/mo) + Spaces blob storage ($5/mo) + +--- + +## 2. Architecture + +### Data Flow (Hybrid Model) + +``` +User Action โ†’ AC Backend (Netlify Functions) + โ”œโ”€โ”€ MongoDB (application data, search, features) + โ”œโ”€โ”€ PDS (identity, ATProto records, federation) + โ””โ”€โ”€ DO Spaces (media blobs โ€” PNG, ZIP, MP4) +``` + +### Key Backend Modules + +| Module | Location | Purpose | +|--------|----------|---------| +| `at.mjs` | `system/backend/at.mjs` | Account CRUD, handle sync | +| `media-atproto.mjs` | `system/backend/media-atproto.mjs` | Unified record creation for all 5 media types | +| `painting-atproto.mjs` | `system/backend/painting-atproto.mjs` | Painting-specific sync with thumbnails | +| `mood-atproto.mjs` | `system/backend/mood-atproto.mjs` | Mood sync | +| `tape-atproto.mjs` | `system/backend/tape-atproto.mjs` | Tape sync with MP4 conversion | +| `news-atproto.mjs` | `system/backend/news-atproto.mjs` | News record sync | + +### Netlify Functions Touching ATProto + +| Function | What It Does | +|----------|-------------| +| `handle.mjs` | Syncs AC handle changes โ†’ PDS handle | +| `mood.mjs` | Creates mood + syncs to PDS | +| `store-kidlisp.mjs` | Stores KidLisp + syncs to PDS | +| `store-piece.mjs` | Stores piece + syncs to PDS | +| `tv.mjs` / `tv-tapes.mjs` | Queries tapes from PDS via XRPC | +| `atproto-user-stats.mjs` | Aggregate user stats from MongoDB | +| `delete-erase-and-forget-me.mjs` | Full account deletion including PDS | + +--- + +## 3. Custom Lexicons + +All live at `at/lexicons/computer/aesthetic/`: + +| Lexicon | Required Fields | Blob? | +|---------|----------------|-------| +| `computer.aesthetic.painting` | slug, code, imageUrl, when, ref | thumbnail (PNG/JPEG, 1MB max) | +| `computer.aesthetic.mood` | mood, when, ref | โ€” | +| `computer.aesthetic.piece` | slug, when, ref | โ€” | +| `computer.aesthetic.kidlisp` | code, source, acUrl, when, ref | โ€” | +| `computer.aesthetic.tape` | slug, code, acUrl, when, ref | video (MP4), thumbnail | +| `computer.aesthetic.news` | headline, when, ref | โ€” | + +### Lexicon Publication Status + +- **DNS TXT record** (`_lexicon.aesthetic.computer`) is planned but status unclear โ€” needs verification. +- Lexicon JSON schemas exist locally but may not be published to the network yet. + +--- + +## 4. Accounts & Identity + +| Account | Handle | DID | Purpose | +|---------|--------|-----|---------| +| Main | `aesthetic.computer` | `did:plc:k3k3wknzkcnekbnyde4dbatz` | Official Bluesky presence | +| Art (guest) | `art.at.aesthetic.computer` | `did:plc:tliuubv7lyv2uiknsjbf4ppw` | Anonymous/guest content | +| Users | `{handle}.at.aesthetic.computer` | `did:plc:*` | Per-user PDS accounts | + +### Account Provisioning Flow + +1. User registers on AC (Auth0) +2. `createAtprotoAccount()` generates invite code, creates PDS account +3. Credentials stored in MongoDB: `users.atproto = { did, handle, password, created }` +4. Handle changes sync via `updateAtprotoHandle()` +5. Account deletion cascades to PDS via `deleteAtprotoAccount()` + +--- + +## 5. Tooling Audit + +### Existing Scripts (Scattered, No Unified CLI) + +**`at/` directory** โ€” standalone query/post tools: +- `query-profile.mjs` โ€” fetch profile for any handle/DID +- `query-posts.mjs` โ€” fetch posts from account +- `post-to-bluesky.mjs` โ€” post to @aesthetic.computer +- `resolve-did.mjs` โ€” resolve DID documents, inspect PDS +- `explore-lexicons.mjs` โ€” browse lexicon schemas +- `share-latest-painting.mjs` / `share-random-painting.mjs` +- `test-mood-api.mjs` / `test-painting-atproto.mjs` / `test-all.mjs` + +**`at/pds/scripts/`** โ€” admin tools (31 scripts): +- Account: `create-account.mjs`, `create-bulk-accounts.mjs`, `recreate-jeffrey.mjs` +- Invites: `generate-invite.mjs` +- Checks: `check-missing-accounts.mjs`, `check-stale-atproto.mjs`, `check-handle-structure.mjs` +- Migration: `migrate-pds-accounts.mjs`, `migrate-atproto-to-nested.mjs` +- Monitoring: `health-check.sh`, `backup.sh` +- Queries: `query-user.mjs`, `explore-collections.mjs`, `inspect-user-schema.mjs` + +**`scripts/atproto/`** โ€” sync/backfill scripts (28 scripts): +- Backfill: `backfill-moods-to-atproto.mjs`, `backfill-paintings-to-atproto.mjs` +- Verify: `verify-paintings-sync.mjs`, `verify-tapes-sync.mjs` +- Compare: `compare-moods-atproto-mongo.mjs` +- Stats: `mood-sync-stats.mjs`, `painting-sync-stats.mjs` +- Sync: `sync-atproto.mjs`, `sync-mongodb-from-atproto.mjs` + +### Problem: No Unified Entry Point + +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: + +- **Hard to discover** โ€” you need to know what exists and where +- **Inconsistent invocation** โ€” some use dotenv, some need env vars, some need SSH +- **No help system** โ€” no way to list available commands +- **Duplicated patterns** โ€” similar arg parsing, PDS URL resolution, etc. in each script + +--- + +## 6. Issues & Improvement Opportunities + +### P0 โ€” Verify & Fix + +- [ ] **PDS health**: Run health check against `at.aesthetic.computer` โ€” confirm it's online and responding +- [ ] **DNS TXT record**: Verify `_lexicon.aesthetic.computer` TXT record exists for lexicon publication +- [ ] **Wildcard DNS**: Confirm `*.at.aesthetic.computer` resolves correctly for user pages +- [ ] **SSL certificates**: Check cert validity and auto-renewal (Caddy should handle this) +- [ ] **@atproto/api version**: System uses `^0.18.0`, at/ experiments use `^0.17.0` โ€” should unify + +### P1 โ€” Build + +- [ ] **`ac-at` CLI** โ€” Unified command-line tool consolidating all AT tooling (see section 7) +- [ ] **Lexicon publication** โ€” Publish lexicon schemas to the network via DNS TXT + hosting +- [ ] **User pages deployment verification** โ€” Confirm user-page.html is being served at subdomains + +### P2 โ€” Improve + +- [ ] **Sync monitoring** โ€” Automated checks for MongoDB โ†” PDS drift +- [ ] **Credential rotation** โ€” PDS user passwords are stored in MongoDB; consider rotation strategy +- [ ] **Blob storage cleanup** โ€” Orphaned blobs on PDS from deleted records +- [ ] **Rate limiting audit** โ€” Current config: `PDS_RATE_LIMIT_ENABLED=true`, `PDS_MAX_ACCOUNTS=100` +- [ ] **Backup verification** โ€” Confirm `backup.sh` runs and backups are restorable +- [ ] **Error handling** โ€” Some sync functions silently continue on failure; add alerting + +### P3 โ€” Future + +- [ ] **Federation** โ€” Custom lexicon records are currently PDS-local; plan for relay/appview integration +- [ ] **OAuth/DPOP** โ€” ATProto is moving toward OAuth; plan migration from password-based auth +- [ ] **Firehose consumer** โ€” `firehose-monitor.mjs` exists but isn't integrated into monitoring +- [ ] **Cross-post from PDS to Bluesky** โ€” Paintings/tapes could auto-post to app.bsky.feed.post + +--- + +## 7. `ac-at` CLI Specification + +Consolidate all AT tooling into a single CLI at `at/cli.mjs`, following the patterns of `memory/cli.mjs` and `papers/cli.mjs`. + +### Commands + +``` +ac-at help Show all commands +ac-at health PDS health check (port from health-check.sh) +ac-at resolve Resolve DID document +ac-at profile Query profile information +ac-at posts [--limit=N] Query posts +ac-at post [--image=path] Post to Bluesky +ac-at records [--limit=N] List records in collection +ac-at lexicons Show AC custom lexicons +ac-at invite Generate PDS invite code +ac-at accounts [--limit=N] List PDS accounts +ac-at account:create Create PDS account for AC user +ac-at account:check Inspect account status +ac-at sync:status Show sync stats across all collections +ac-at sync:paintings [--dry-run] Backfill paintings to PDS +ac-at sync:moods [--dry-run] Backfill moods to PDS +ac-at sync:verify Compare MongoDB โ†” PDS for a collection +``` + +### npm scripts + +```json +"at": "node at/cli.mjs", +"at:health": "node at/cli.mjs health", +"at:resolve": "node at/cli.mjs resolve", +"at:profile": "node at/cli.mjs profile", +"at:post": "node at/cli.mjs post", +"at:invite": "node at/cli.mjs invite" +``` + +--- + +## 8. Dependency Versions + +| Package | system/ | at/ | Notes | +|---------|---------|-----|-------| +| `@atproto/api` | `^0.18.0` | `^0.17.0` | Should align to latest | +| `@atproto/identity` | โ€” | `^0.4.3` | Only in experiments | +| `@atproto/lexicon` | โ€” | `^0.4.2` | Only in experiments | +| `@atproto/syntax` | โ€” | `^0.3.1` | Only in experiments | +| `@atproto/xrpc` | โ€” | `^0.6.5` | Only in experiments | +| `dotenv` | (via netlify) | `^16.4.5` | CLI scripts need this | + +--- + +## 9. Environment Variables + +### Required for PDS operations + +``` +PDS_URL=https://at.aesthetic.computer # PDS endpoint +PDS_ADMIN_PASSWORD= # Admin API auth +``` + +### Required for Bluesky posting + +``` +BSKY_IDENTIFIER=aesthetic.computer # Bluesky handle +BSKY_APP_PASSWORD= # From bsky.app/settings/app-passwords +BSKY_SERVICE=https://bsky.social # Bluesky API endpoint +``` + +### PDS Server Config (on droplet) + +``` +PDS_HOSTNAME=pds.aesthetic.computer +PDS_ADMIN_EMAIL=me@jas.life +PDS_PORT=3000 +PDS_BLOBSTORE_S3_BUCKET=aesthetic-pds-blobs +PDS_INVITE_REQUIRED=true +PDS_MAX_ACCOUNTS=100 +``` + +--- + +## 10. File Map + +``` +at/ +โ”œโ”€โ”€ cli.mjs โ† TO BUILD: unified CLI +โ”œโ”€โ”€ package.json โ† @atproto dependencies +โ”œโ”€โ”€ lexicons/computer/aesthetic/ โ† 6 lexicon JSON schemas +โ”œโ”€โ”€ query-profile.mjs โ† standalone tools (to consolidate) +โ”œโ”€โ”€ query-posts.mjs +โ”œโ”€โ”€ post-to-bluesky.mjs +โ”œโ”€โ”€ resolve-did.mjs +โ”œโ”€โ”€ explore-lexicons.mjs +โ”œโ”€โ”€ pds/ +โ”‚ โ”œโ”€โ”€ config/pds.env.example +โ”‚ โ”œโ”€โ”€ scripts/ โ† 31 admin scripts +โ”‚ โ””โ”€โ”€ deployment/ โ† DigitalOcean provisioning +โ”œโ”€โ”€ scripts/atproto/ โ† 28 sync/verify scripts (also at repo root) +โ”œโ”€โ”€ user-page.html โ† served at *.at.aesthetic.computer +โ””โ”€โ”€ deploy-user-pages.fish + +system/backend/ +โ”œโ”€โ”€ at.mjs โ† core PDS account management +โ”œโ”€โ”€ media-atproto.mjs โ† unified record creation +โ”œโ”€โ”€ painting-atproto.mjs โ† painting sync +โ”œโ”€โ”€ mood-atproto.mjs โ† mood sync +โ”œโ”€โ”€ tape-atproto.mjs โ† tape sync +โ””โ”€โ”€ news-atproto.mjs โ† news sync + +system/netlify/functions/ +โ”œโ”€โ”€ handle.mjs โ† handle โ†’ PDS sync +โ”œโ”€โ”€ mood.mjs โ† mood creation + PDS +โ”œโ”€โ”€ store-kidlisp.mjs โ† kidlisp + PDS +โ”œโ”€โ”€ store-piece.mjs โ† piece + PDS +โ”œโ”€โ”€ tv.mjs / tv-tapes.mjs โ† query tapes from PDS +โ”œโ”€โ”€ atproto-user-stats.mjs โ† stats aggregation +โ””โ”€โ”€ delete-erase-and-forget-me.mjs โ† cascade delete +``` diff --git a/system/backend/bluesky-engagement.mjs b/system/backend/bluesky-engagement.mjs index bad5988020..e8f5d63365 100644 --- a/system/backend/bluesky-engagement.mjs +++ b/system/backend/bluesky-engagement.mjs @@ -2,7 +2,7 @@ // Fetch engagement (likes, reposts, replies) from Bluesky for mirrored moods // 2026.01.28 -import { BskyAgent } from "@atproto/api"; +import { AtpAgent } from "@atproto/api"; import { shell } from "./shell.mjs"; const BSKY_SERVICE = "https://bsky.social"; @@ -19,7 +19,7 @@ export async function fetchBlueskyEngagement(blueskyUri) { try { // Public API - no auth needed for reading - const agent = new BskyAgent({ service: BSKY_SERVICE }); + const agent = new AtpAgent({ service: BSKY_SERVICE }); // Get the post thread which includes engagement data const response = await agent.getPostThread({ @@ -99,7 +99,7 @@ function blueskyUriToWebUrl(atUri) { */ export async function resolveDidToHandle(did) { try { - const agent = new BskyAgent({ service: BSKY_SERVICE }); + const agent = new AtpAgent({ service: BSKY_SERVICE }); const response = await agent.getProfile({ actor: did }); return response.data.handle; } catch { diff --git a/system/backend/bluesky-mirror.mjs b/system/backend/bluesky-mirror.mjs index 3c09f7ea58..910e531ea5 100644 --- a/system/backend/bluesky-mirror.mjs +++ b/system/backend/bluesky-mirror.mjs @@ -2,7 +2,7 @@ // Mirror moods to Bluesky @aesthetic.computer account // 2026.01.28 -import { BskyAgent } from "@atproto/api"; +import { AtpAgent } from "@atproto/api"; import { shell } from "./shell.mjs"; // Cache for Bluesky credentials from MongoDB @@ -66,7 +66,7 @@ export async function postMoodToBluesky(database, moodText, handle, atprotoRkey) } try { - const agent = new BskyAgent({ service: creds.service }); + const agent = new AtpAgent({ service: creds.service }); shell.log(`๐Ÿฆ‹ Logging into Bluesky as @${creds.identifier}...`); await agent.login({ @@ -137,7 +137,7 @@ export async function deleteMoodFromBluesky(database, blueskyRkey) { } try { - const agent = new BskyAgent({ service: creds.service }); + const agent = new AtpAgent({ service: creds.service }); await agent.login({ identifier: creds.identifier, diff --git a/system/package-lock.json b/system/package-lock.json index 045e80c7e8..68947fa4b7 100644 --- a/system/package-lock.json +++ b/system/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@atproto/api": "^0.18.0", + "@atproto/api": "^0.19.4", "@ffmpeg/core": "^0.12.10", "@ffmpeg/ffmpeg": "^0.12.15", "@geckos.io/client": "^3.0.2", @@ -187,14 +187,14 @@ } }, "node_modules/@atproto/api": { - "version": "0.18.21", - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.18.21.tgz", - "integrity": "sha512-s35MIJerGT/pKe2xJtKKswqlIr/ola2r2iURBKBL0Mk1OKe6jP4YvTMh1N2d2PEANFzNNTbKoDaLfJPo2Uvc/w==", + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.19.4.tgz", + "integrity": "sha512-fYNM62vdXxer0h8a9Jzl4/ag9uFIe0nTO+LkC6KTlx1yUDigrAoQMMbllIiCWj62GhUMxAkHabk/BZjjVAfKng==", "license": "MIT", "dependencies": { - "@atproto/common-web": "^0.4.16", - "@atproto/lexicon": "^0.6.1", - "@atproto/syntax": "^0.4.3", + "@atproto/common-web": "^0.4.18", + "@atproto/lexicon": "^0.6.2", + "@atproto/syntax": "^0.5.1", "@atproto/xrpc": "^0.7.7", "await-lock": "^2.2.2", "multiformats": "^9.9.0", @@ -203,21 +203,21 @@ } }, "node_modules/@atproto/common-web": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.16.tgz", - "integrity": "sha512-Ufvaff5JgxUyUyTAG0/3o7ltpy3lnZ1DvLjyAnvAf+hHfiK7OMQg+8byr+orN+KP9MtIQaRTsCgYPX+PxMKUoA==", + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.19.tgz", + "integrity": "sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw==", "license": "MIT", "dependencies": { - "@atproto/lex-data": "^0.0.11", - "@atproto/lex-json": "^0.0.11", - "@atproto/syntax": "^0.4.3", + "@atproto/lex-data": "^0.0.14", + "@atproto/lex-json": "^0.0.14", + "@atproto/syntax": "^0.5.1", "zod": "^3.23.8" } }, "node_modules/@atproto/lex-data": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.11.tgz", - "integrity": "sha512-4+KTtHdqwlhiTKA7D4SACea4jprsNpCQsNALW09wsZ6IHhCDGO5tr1cmV+QnLYe3G3mu1E1yXHXbPUHrUUDT/A==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.14.tgz", + "integrity": "sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ==", "license": "MIT", "dependencies": { "multiformats": "^9.9.0", @@ -227,32 +227,32 @@ } }, "node_modules/@atproto/lex-json": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.11.tgz", - "integrity": "sha512-2IExAoQ4KsR5fyPa1JjIvtR316PvdgRH/l3BVGLBd3cSxM3m5MftIv1B6qZ9HjNiK60SgkWp0mi9574bTNDhBQ==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.14.tgz", + "integrity": "sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww==", "license": "MIT", "dependencies": { - "@atproto/lex-data": "^0.0.11", + "@atproto/lex-data": "^0.0.14", "tslib": "^2.8.1" } }, "node_modules/@atproto/lexicon": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.1.tgz", - "integrity": "sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.2.tgz", + "integrity": "sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==", "license": "MIT", "dependencies": { - "@atproto/common-web": "^0.4.13", - "@atproto/syntax": "^0.4.3", + "@atproto/common-web": "^0.4.18", + "@atproto/syntax": "^0.5.0", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "node_modules/@atproto/syntax": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.3.tgz", - "integrity": "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", "license": "MIT", "dependencies": { "tslib": "^2.8.1" diff --git a/system/package.json b/system/package.json index fe4cfd2fbf..f167ddb16f 100644 --- a/system/package.json +++ b/system/package.json @@ -45,7 +45,7 @@ }, "homepage": "https://github.com/whistlegraph/system#readme", "dependencies": { - "@atproto/api": "^0.18.0", + "@atproto/api": "^0.19.4", "@ffmpeg/core": "^0.12.10", "@ffmpeg/ffmpeg": "^0.12.15", "@geckos.io/client": "^3.0.2", diff --git a/system/public/aesthetic.computer/disks/blank.mjs b/system/public/aesthetic.computer/disks/blank.mjs index ec7f7c2a57..0cea79d27e 100644 --- a/system/public/aesthetic.computer/disks/blank.mjs +++ b/system/public/aesthetic.computer/disks/blank.mjs @@ -115,7 +115,7 @@ function paint($) { const availW = w * 0.42; const availH = (contentBottom - 20) * 0.32; const size = min(availW, availH); - const fov = 260; + const fov = size * 5.5; // Slow turntable + gentle tilt variation (see it from all angles) const ay = frame * 0.006; @@ -237,6 +237,7 @@ function paint($) { const viewDirY = sin(ax); const viewDirZ = cos(ay) * cos(ax); + const debugNormals = []; const addFaces = (verts3d, proj, color, tag) => { const frontFaces = new Set(); // Compute box center for outward normal correction @@ -259,8 +260,18 @@ function paint($) { // Ensure normal points OUTWARD (away from box center) const outDot = nx * (fcx - cx3) + ny * (fcy - cy3) + nz * (fcz - cz3); if (outDot < 0) { nx = -nx; ny = -ny; nz = -nz; } + // Normalize + const nLen = sqrt(nx * nx + ny * ny + nz * nz) || 1; + nx /= nLen; ny /= nLen; nz /= nLen; // Dot with view direction โ€” negative = faces camera (opposes view dir) const dot = nx * viewDirX + ny * viewDirY + nz * viewDirZ; + // Store debug normal (all faces, front or back) + const nScale = 0.4; + debugNormals.push({ + from: [fcx, fcy, fcz], + to: [fcx + nx * nScale, fcy + ny * nScale, fcz + nz * nScale], + front: dot < 0, tag, fi, + }); if (dot >= 0) continue; // faces away from camera โ†’ skip frontFaces.add(fi); const z = (proj[a][2] + proj[b][2] + proj[c][2] + proj[d][2]) / 4; @@ -278,12 +289,20 @@ function paint($) { const kbTR = project([hw - kbInset, -0.001, kbInset]); const kbBL = project([-hw + kbInset, -0.001, 2 * hd - kbInset * 3]); const kbBR = project([hw - kbInset, -0.001, 2 * hd - kbInset * 3]); - const ke1x = kbTR[0] - kbTL[0], ke1y = kbTR[1] - kbTL[1]; - const ke2x = kbBL[0] - kbTL[0], ke2y = kbBL[1] - kbTL[1]; - // Store keyboard data to draw after sorted loop (always on top of base) - let kbVisible = ke1x * ke2y - ke1y * ke2x < 0; + // Screen-space winding check (same approach as lid screen content) + const kbE1x = kbTR[0] - kbTL[0], kbE1y = kbTR[1] - kbTL[1]; + const kbE2x = kbBL[0] - kbTL[0], kbE2y = kbBL[1] - kbTL[1]; + const kbCross = kbE1x * kbE2y - kbE1y * kbE2x; + // Smooth alpha from cross product magnitude (same as screen content) + const kbMaxCross = size * size * 0.5; + const kbFacingAmount = kbCross < 0 ? min(1, abs(kbCross) / kbMaxCross) : 0; + // Fade with hinge angle (smooth open/close) and lid occlusion + const hingeFade = min(1, max(0, (hingeAngle - 0.1) / 0.5)); + const lidOccludes = hingeAngle < PI && lidFrontFaces.has(1); + const kbAlpha = floor(kbFacingAmount * hingeFade * (lidOccludes ? 0 : 1) * 255); + const kbFacing = kbAlpha > 2; const kbKeys = []; - if (kbVisible) { + if (kbFacing) { // 6-row ThinkPad keyboard layout (key counts per row) // Row 0: Fn keys (14 keys: Esc + F1-F12 + Del) // Row 1: Number row (14 keys: ` 1-0 - = Bksp) @@ -395,22 +414,32 @@ function paint($) { } } - // โŒจ๏ธ Keyboard keys โ€” only draw when base top face (faceQuads[0]) is front-facing - // and lid isn't in front of base - const baseTopVisible = baseFrontFaces.has(0); - const lidAvgZ = projLid.reduce((s, v) => s + v[2], 0) / projLid.length; - const baseAvgZ = projBase.reduce((s, v) => s + v[2], 0) / projBase.length; - const kbNotOccluded = baseTopVisible && lidAvgZ >= baseAvgZ; - if (kbVisible && kbNotOccluded) { + // ๐Ÿ” Debug: draw face normals (green = front-facing, red = back-facing) + for (const dn of debugNormals) { + const p0 = project(dn.from); + const p1 = project(dn.to); + if (dn.front) { + ink(0, 255, 0, 200); + } else { + ink(255, 0, 0, 120); + } + line(p0[0], p0[1], p1[0], p1[1]); + // Dot at tip + ink(255, 255, 0, 220).box(p1[0] - 1, p1[1] - 1, 3, 3); + } + + // โŒจ๏ธ Keyboard keys โ€” smooth fade based on facing + hinge + occlusion + if (kbFacing) { for (const key of kbKeys) { const [[x0, y0], [x1, y1], [x2, y2], [x3, y3]] = key.pts; const c = key.color || keyColor; - ink(c[0], c[1], c[2]); + ink(c[0], c[1], c[2], kbAlpha); tri(x0, y0, x1, y1, x2, y2); tri(x0, y0, x2, y2, x3, y3); // White blinky wireframe outline const blink = sin(frame * 0.08 + x0 * 0.3 + y0 * 0.7) * 0.5 + 0.5; - ink(255, 255, 255, floor(30 + blink * 50)); + const wireAlpha = floor((30 + blink * 50) * kbAlpha / 255); + ink(255, 255, 255, wireAlpha); line(x0, y0, x1, y1); line(x1, y1, x2, y2); line(x2, y2, x3, y3); diff --git a/system/public/aesthetic.computer/disks/os.mjs b/system/public/aesthetic.computer/disks/os.mjs index d1859df5f8..d703e313bc 100644 --- a/system/public/aesthetic.computer/disks/os.mjs +++ b/system/public/aesthetic.computer/disks/os.mjs @@ -61,7 +61,7 @@ let showTokenHint = false; // Build telemetry let activeBuild = null; // { id, status, stage, percent, ref, error } let buildLogLines = []; // last few log lines -const MAX_BUILD_LOGS = 6; +const MAX_BUILD_LOGS = 10; let buildPollTimer = null; // Color palettes for dark/light mode (deprecated builds use red tones) @@ -185,9 +185,22 @@ function fetchBuildStatus(needsPaint) { .then(data => { if (data.active) { activeBuild = data.active; - needsPaint(); + // Fetch log lines for active build + if (data.activeJobId) { + fetch(OVEN_BASE() + "/native-build/" + data.activeJobId + "?logs=true&tail=10") + .then(r => r.json()) + .then(detail => { + if (detail.logs) { + buildLogLines = detail.logs.slice(-MAX_BUILD_LOGS).map(l => l.line || l); + } + needsPaint(); + }) + .catch(() => needsPaint()); + } else { + needsPaint(); + } } else { - activeBuild = null; + if (activeBuild) { activeBuild = null; buildLogLines = []; needsPaint(); } } }) .catch(() => {}); @@ -217,14 +230,32 @@ function connectOvenWs(ui, needsPaint) { needsPaint(); } if (msg.type === "os:build-progress" && msg.build) { - activeBuild = msg.build; - if (msg.build.status === "success" || msg.build.status === "failed" || msg.build.status === "cancelled") { + const b = msg.build; + // Lightweight per-line message (has .line but no full snapshot fields) + if (b.line && !b.status) { + if (buildLogLines.length >= MAX_BUILD_LOGS) buildLogLines.shift(); + buildLogLines.push(b.line); + // Update stage/percent from lightweight msg + if (activeBuild) { + if (b.stage) activeBuild.stage = b.stage; + if (b.percent) activeBuild.percent = b.percent; + } + needsPaint(); + return; + } + // Full snapshot + activeBuild = b; + if (b.recentLines) { + buildLogLines = b.recentLines.slice(-MAX_BUILD_LOGS); + } + if (b.status === "success" || b.status === "failed" || b.status === "cancelled") { // Build done โ€” refresh releases list setTimeout(() => { activeBuild = null; + buildLogLines = []; fetchReleases(ui, needsPaint); needsPaint(); - }, 3000); + }, 5000); } needsPaint(); } @@ -406,7 +437,8 @@ function paint($) { const rawStage = activeBuild.stage || "starting"; const isCLBuild = rawStage.startsWith("cl-"); const buildVariant = isCLBuild ? "Common Lisp" : "C"; - sectionHeader("Building (" + buildVariant + ")", dark ? [20, 28, 20] : [215, 235, 215], dark ? [14, 20, 14] : [228, 240, 228], 120); + const logH = buildLogLines.length * (matrixH + 1) + 8; + sectionHeader("Building (" + buildVariant + ")", dark ? [20, 28, 20] : [215, 235, 215], dark ? [14, 20, 14] : [228, 240, 228], 120 + logH); // Status line: stage + percentage const stageLabel = isCLBuild ? rawStage.slice(3) : rawStage; @@ -453,6 +485,18 @@ function paint($) { y += rowH + 2; } + // Live log lines + if (buildLogLines.length > 0) { + y += 4; + for (const line of buildLogLines) { + const maxChars = Math.floor(wrapW / matrixW); + const display = line.length > maxChars ? line.slice(0, maxChars - 1) + "~" : line; + ink(...C.instText); + $.write(display, { x: pad, y, wrap: wrapW }, undefined, undefined, false, "MatrixChunky8"); + y += matrixH + 1; + } + } + y += isMobile ? 10 : 6; } -- 2.51.2