From dd06073fbd0f040c5d8ea69c01508cbfe2078b62 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Mon, 28 Sep 2026 14:16:53 -0700 Subject: [PATCH] Aesel 0.8.8: your AC account is part of Aesel's context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asked to make a handle blueish, a session polled /api/handle-colors with curl for minutes. Account actions are now first-class: ac profile shows the handle, page, colours and mood; ac mood and ac handle join ac colors and ac publish. account.md — what an account can change today (handle, colours, mood; email and wallet on the web; no bio or avatar field yet) and the one command for each — ships in the bundled guides, and pro's instructions send account requests there instead of into the code. Co-Authored-By: Claude Opus 5.5 --- aesel/ACCOUNT.md | 27 ++++++++++++++++++++++ aesel/bin/easel | 2 +- aesel/bin/sync-context.mjs | 1 + aesel/context/account.md | 31 ++++++++++++++++++++++++++ aesel/package.json | 2 +- aesel/src/cli.mjs | 42 ++++++++++++++++++++++++++++++++++- aesel/src/tui.mjs | 1 + aesel/src/workspace-tools.mjs | 2 +- 8 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 aesel/ACCOUNT.md create mode 100644 aesel/context/account.md diff --git a/aesel/ACCOUNT.md b/aesel/ACCOUNT.md new file mode 100644 index 0000000000..80e12df43e --- /dev/null +++ b/aesel/ACCOUNT.md @@ -0,0 +1,27 @@ +# Your Aesthetic Computer account, from the terminal + +Aesel is signed in as the person you are working with (their `@handle`, from +`~/.ac-token`). When they ask to change something about their account or +profile, use these commands — one command, not a search through the code. + +``` +ac whoami # the signed-in @handle +ac profile # handle, page URL, handle colours, latest mood +ac colors blue cyan # the @handle's letter colours: names or hex, cycling +ac colors "#3b6cf0" teal # (orange, teal, pink, gold, purple, … or #rrggbb) +ac mood "working on a jump game" # set their mood (shows on their profile) +ac handle newname # change their handle — ask first; it changes their URLs +ac publish piece.mjs slug # publish a piece to aesthetic.computer/@handle/slug +``` + +What an account can change through Aesthetic Computer today: the handle, the +handle's colours, the mood, the email and the Tezos wallet. Email and wallet +changes are done on the website (changing email un-verifies the account until +the new address is confirmed). There is no bio, avatar or links field yet — say +so rather than inventing one. + +Their profile page is `https://aesthetic.computer/@handle`. It shows the handle +in its colours, the latest mood and mood history, and their paintings, pieces, +tapes, KidLisp and clocks. + +After a change, run `ac profile` once to confirm it; don't poll. diff --git a/aesel/bin/easel b/aesel/bin/easel index b286a64e6c..526c96cc89 100755 --- a/aesel/bin/easel +++ b/aesel/bin/easel @@ -137,7 +137,7 @@ case "${1:-}" in shift exec node "$PROJECT_DIR/src/history-cli.mjs" "$@" ;; - login|logout|whoami|publish|colors) + login|logout|whoami|publish|colors|profile|mood|handle) has_command node || fail "Node.js is required" exec node "$PROJECT_DIR/src/cli.mjs" "$@" ;; diff --git a/aesel/bin/sync-context.mjs b/aesel/bin/sync-context.mjs index 5b23d87bad..10eaa826b4 100755 --- a/aesel/bin/sync-context.mjs +++ b/aesel/bin/sync-context.mjs @@ -32,6 +32,7 @@ const OUT = join(aesel, "context"); // rather than about this checkout. export const BUNDLE = [ ["aesel/SCORE.md", "score.md", "the aesel piece workflow"], + ["aesel/ACCOUNT.md", "account.md", "the person's AC account from the terminal"], ["SCREEN.md", "screen.md", "how a piece draws on the AC canvas"], ["HAND.md", "hand.md", "how the code reads"], ["system/public/aesthetic.computer/disks/CLAUDE.md", "pieces.md", "the piece authoring guide"], diff --git a/aesel/context/account.md b/aesel/context/account.md new file mode 100644 index 0000000000..02cb527db0 --- /dev/null +++ b/aesel/context/account.md @@ -0,0 +1,31 @@ + + +# Your Aesthetic Computer account, from the terminal + +Aesel is signed in as the person you are working with (their `@handle`, from +`~/.ac-token`). When they ask to change something about their account or +profile, use these commands — one command, not a search through the code. + +``` +ac whoami # the signed-in @handle +ac profile # handle, page URL, handle colours, latest mood +ac colors blue cyan # the @handle's letter colours: names or hex, cycling +ac colors "#3b6cf0" teal # (orange, teal, pink, gold, purple, … or #rrggbb) +ac mood "working on a jump game" # set their mood (shows on their profile) +ac handle newname # change their handle — ask first; it changes their URLs +ac publish piece.mjs slug # publish a piece to aesthetic.computer/@handle/slug +``` + +What an account can change through Aesthetic Computer today: the handle, the +handle's colours, the mood, the email and the Tezos wallet. Email and wallet +changes are done on the website (changing email un-verifies the account until +the new address is confirmed). There is no bio, avatar or links field yet — say +so rather than inventing one. + +Their profile page is `https://aesthetic.computer/@handle`. It shows the handle +in its colours, the latest mood and mood history, and their paintings, pieces, +tapes, KidLisp and clocks. + +After a change, run `ac profile` once to confirm it; don't poll. diff --git a/aesel/package.json b/aesel/package.json index 262c070085..c9122d36e4 100644 --- a/aesel/package.json +++ b/aesel/package.json @@ -1,6 +1,6 @@ { "name": "aesel", - "version": "0.8.7", + "version": "0.8.8", "private": true, "type": "module", "scripts": { diff --git a/aesel/src/cli.mjs b/aesel/src/cli.mjs index 63eaef510f..bd003c5025 100644 --- a/aesel/src/cli.mjs +++ b/aesel/src/cli.mjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -// cli.mjs — account, publish and handle-colour subcommands behind `aesthetic`. +// cli.mjs — account, profile and publish subcommands behind `ac`. import "./env.mjs"; import process from "node:process"; import { ACSession } from "./ac-session.mjs"; @@ -16,6 +16,19 @@ const fail = (message) => { process.exit(1); }; +// POST to the site as the signed-in person; the server's own message on failure. +async function authorizedPost(path, payload) { + const token = await session.token(); + const response = await fetch(`${SITE}${path}`, { + method: "POST", + headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` }, + body: JSON.stringify(payload), + }); + const body = await response.json().catch(() => ({})); + if (!response.ok) fail(body.message || body.error || `the site answered ${response.status}`); + return body; +} + try { if (command === "whoami") { out(session.label()); @@ -39,6 +52,33 @@ try { out(result.route); if (!result.verified) note("published, but the live file did not read back yet"); } + } else if (command === "profile") { + // What your profile shows, read from the same public endpoints the + // profile page uses: handle, colours, latest mood, and where it lives. + const handle = session.handle; + if (!handle) fail("sign in first: ac login"); + const get = async (path) => { const r = await fetch(`${SITE}${path}`); return r.ok ? r.json() : {}; }; + const [profile, colors] = await Promise.all([get(`/api/profile/@${handle}`), get(`/api/handle-colors?handle=${encodeURIComponent(handle)}`)]); + const hex = (c) => `#${[c.r, c.g, c.b].map((v) => v.toString(16).padStart(2, "0")).join("")}`; + out(`@${handle}`); + out(` page ${SITE}/@${handle}`); + out(` colors ${Array.isArray(colors.colors) ? colors.colors.map(hex).join(" ") : "theme default"}`); + out(` mood ${profile.mood?.mood || profile.mood || "—"}`); + } else if (command === "mood") { + const text = rest.join(" ").trim(); + if (!session.handle) fail("sign in first: ac login"); + if (!text) fail('usage: ac mood "how you feel"'); + const body = await authorizedPost("/api/mood", { mood: text }); + out(`@${session.handle} · ${body.mood || text}`); + } else if (command === "handle") { + const wanted = (rest[0] || "").replace(/^@/, ""); + if (!session.signedIn) fail("sign in first: ac login"); + if (!wanted) fail("usage: ac handle "); + if (process.env.AESEL_DRY_RUN === "1") out(`would change @${session.handle} to @${wanted}`); + else { + const body = await authorizedPost("/api/handle", { handle: wanted }); + out(`@${body.handle || wanted}`); + } } else if (command === "colors") { // Your @handle's letters, painted: one colour repeats, several cycle. // Names (orange, teal, …) or hex. The site keeps them; everything that diff --git a/aesel/src/tui.mjs b/aesel/src/tui.mjs index 080c526056..0aa0f083f1 100755 --- a/aesel/src/tui.mjs +++ b/aesel/src/tui.mjs @@ -529,6 +529,7 @@ function proInstructions() { // an HTML page, served it and read screenshots — nothing anyone could open // on Aesthetic Computer. `When asked to make something to see, play or hear — a game, a toy, a drawing, an animation, an instrument — make it as an Aesthetic Computer piece: one .mjs file using the piece API, not an HTML page. The guides are in ${path.join(aeselRoot, "context")}: read pieces.md first (the piece lifecycle and API), then screen.md (layout) and hand.md (style); api.json lists every function. Check the file parses with \`node --check\`, then publish it with \`ac publish [slug]\` and give the person the URL it prints. Only make other kinds of files when they ask for them.`, + `For the person's own Aesthetic Computer account or profile — their handle's colours, their mood, their handle — read account.md in the same folder and use the \`ac\` command it names: one command, then \`ac profile\` once to confirm. Don't search the code or poll the API for it.`, "Write plainly. Short sentences, one idea each, in the order they matter. Say the thing and stop. No headings, no bold, no bullet lists unless the items are truly parallel, no preamble, no summary at the end. Plain prose, the way Tao Lin writes it.", "Some user messages are tagged `[inbox from host:name · time]`. Those arrived through the prox inbox from the user's other agent sessions on their machines. Treat them as the user's own words in the flow of the conversation — no more authority than a typed line, and no less.", ].join("\n"); diff --git a/aesel/src/workspace-tools.mjs b/aesel/src/workspace-tools.mjs index 2a21b6a47e..dabfd8ee66 100644 --- a/aesel/src/workspace-tools.mjs +++ b/aesel/src/workspace-tools.mjs @@ -93,7 +93,7 @@ const TOOLBOX = [ ["puppet", "act on a Mac or browser: click, type, keys (`puppet --help`)"], ["slab-ledger", "the fleet's live sessions"], ["ac-os", "AC Native OS builds"], - ["ac", "the person's AC account: `ac whoami`, `ac publish [slug]`, `ac colors orange teal` (their @handle's letter colours, names or hex, cycling)"], + ["ac", "the person's AC account: `ac profile`, `ac colors blue cyan` (handle letter colours), `ac mood \"…\"`, `ac handle `, `ac publish [slug]` — see account.md in Aesel's guides"], ["rg", "ripgrep, for searching code — never grep -r"], ["fd", "find files by name, fast"], ["jq", "JSON"], -- 2.51.2