diff --git a/.letta/settings.local.json b/.letta/settings.local.json index 1df0f8d..657c433 100644 --- a/.letta/settings.local.json +++ b/.letta/settings.local.json @@ -3,12 +3,12 @@ "sessionsByServer": { "api.letta.com": { "agentId": "agent-e483dcd2-a0dc-45a7-bdf8-48c2bcfffd11", - "conversationId": "conv-03be1458-8e65-4140-8eac-977c400d7f02" + "conversationId": "default" } }, "lastSession": { "agentId": "agent-e483dcd2-a0dc-45a7-bdf8-48c2bcfffd11", - "conversationId": "conv-03be1458-8e65-4140-8eac-977c400d7f02" + "conversationId": "default" }, "conversationGoalToolsByServer": { "api.letta.com": { diff --git a/ATPROTO.md b/ATPROTO.md index 4ec523d..5dc62a8 100644 --- a/ATPROTO.md +++ b/ATPROTO.md @@ -227,11 +227,10 @@ chart.song.uri -> com.derakkuma.song Publish lexicons: ```bash -ATP_APP_PASSWORD=... REPO_DID= \ - deploy/happyview/scripts/publish_lexicon_records.py +deploy/happyview/scripts/publish-lexicons.sh ``` -`publish_lexicon_records.py` uses the same auth conventions as the catalog publisher: it resolves `PDS_URL` from `REPO_DID` when omitted, creates a session from `ATP_APP_PASSWORD`, and still accepts `ATP_AUTH_TOKEN` as an escape hatch. +`publish-lexicons.sh` publishes `lexicons/schema-records/com.derakkuma.*.json` with `goat lex publish` using the current `goat` login session. Publish the catalog: diff --git a/deploy/happyview/README.md b/deploy/happyview/README.md index 48b68a1..ab7600e 100644 --- a/deploy/happyview/README.md +++ b/deploy/happyview/README.md @@ -58,9 +58,8 @@ journalctl -u happyview -f Run from the Derakkuma repo root with the lexicon authority account's app password: ```bash -ATP_APP_PASSWORD=... REPO_DID= deploy/happyview/scripts/publish_lexicon_records.py +deploy/happyview/scripts/publish-lexicons.sh ``` This publishes `lexicons/schema-records/com.derakkuma.*.json` as -`com.atproto.lexicon.schema` records. `PDS_URL` is resolved from `REPO_DID` when -omitted; `ATP_AUTH_TOKEN` is still accepted if you already have an access JWT. +`com.atproto.lexicon.schema` records using the current `goat` login session. diff --git a/deploy/happyview/scripts/install_happyview_social_lexicons.py b/deploy/happyview/scripts/install_happyview_social_lexicons.py index ecd60f2..3793d50 100644 --- a/deploy/happyview/scripts/install_happyview_social_lexicons.py +++ b/deploy/happyview/scripts/install_happyview_social_lexicons.py @@ -6,6 +6,7 @@ from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent LUA_DIR = SCRIPT_DIR.parent / "lexicons" +RECORD_LEXICON_DIR = SCRIPT_DIR.parents[2] / "lexicons" / "schema-records" LEXICONS = { "com.derakkuma.getFriendsPlays": { @@ -109,6 +110,27 @@ def load_lua_script(nsid: str) -> str: def main() -> None: con = sqlite3.connect("data/happyview.db") now = datetime.now(timezone.utc).isoformat() + for path in sorted(RECORD_LEXICON_DIR.glob("com.derakkuma.*.json")): + lexicon = json.loads(path.read_text()) + nsid = lexicon["id"] + con.execute( + """ + INSERT INTO lexicons (id, revision, lexicon_json, backfill, target_collection, action, script, source, created_at, updated_at) + VALUES (?, 1, ?, 1, ?, NULL, NULL, ?, ?, ?) + ON CONFLICT(id) DO UPDATE SET + lexicon_json = excluded.lexicon_json, + backfill = excluded.backfill, + target_collection = excluded.target_collection, + action = excluded.action, + script = excluded.script, + revision = lexicons.revision + 1, + source = excluded.source, + updated_at = excluded.updated_at + """, + (nsid, json.dumps(lexicon, separators=(",", ":")), nsid, "schema-record", now, now), + ) + print("upserted", nsid) + for nsid, lexicon in LEXICONS.items(): script = load_lua_script(nsid) con.execute( @@ -137,6 +159,12 @@ def main() -> None: tuple(LEXICONS.keys()), ).fetchone()[0], ) + print( + "record lexicons", + con.execute( + "select count(*) from lexicons where source = 'schema-record'" + ).fetchone()[0], + ) if __name__ == "__main__": diff --git a/deploy/happyview/scripts/publish-lexicons.sh b/deploy/happyview/scripts/publish-lexicons.sh new file mode 100755 index 0000000..6e310dd --- /dev/null +++ b/deploy/happyview/scripts/publish-lexicons.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" +LEXICON_DIR="${LEXICON_DIR:-$ROOT/lexicons/schema-records}" + +if ! command -v goat >/dev/null 2>&1; then + echo "goat is required" >&2 + exit 127 +fi + +shopt -s nullglob +records=("$LEXICON_DIR"/com.derakkuma.*.json) +shopt -u nullglob + +if (( ${#records[@]} == 0 )); then + echo "no lexicon schema records found in $LEXICON_DIR" >&2 + exit 2 +fi + +printf 'publishing %s lexicons from %s\n' "${#records[@]}" "$LEXICON_DIR" +goat lex publish "${records[@]}" diff --git a/deploy/happyview/scripts/publish_lexicon_records.py b/deploy/happyview/scripts/publish_lexicon_records.py deleted file mode 100755 index 76288ce..0000000 --- a/deploy/happyview/scripts/publish_lexicon_records.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -"""Publish Derakkuma lexicon schema records to an ATProto PDS. - -Environment: - ATP_APP_PASSWORD app password for the lexicon authority account - ATP_IDENTIFIER lexicon authority handle or DID; defaults to REPO_DID - ATP_AUTH_PDS_URL PDS URL for createSession; defaults to PDS_URL / resolved repo PDS - ATP_AUTH_TOKEN optional pre-created access token; skips createSession when set - REPO_DID repo DID to publish lexicon records to (required) - PDS_URL PDS URL for putRecord; if omitted, resolved from REPO_DID - LEXICON_DIR schema record directory; defaults to lexicons/schema-records - ATP_REQUEST_TIMEOUT per-request timeout in seconds; defaults to 30 -""" -from __future__ import annotations - -from pathlib import Path -import json -import os -import sys - -ROOT = Path(__file__).resolve().parents[3] -sys.path.insert(0, str(ROOT / "scripts")) - -from lib.atproto_publish import get_or_create_auth_token, put_record, resolve_pds - -LEXICON_COLLECTION = "com.atproto.lexicon.schema" -DEFAULT_LEXICON_DIR = "lexicons/schema-records" - - -def lexicon_files() -> list[Path]: - lexicon_dir = Path(os.environ.get("LEXICON_DIR", DEFAULT_LEXICON_DIR)) - return sorted(lexicon_dir.glob("com.derakkuma.*.json")) - - -def main() -> int: - repo = os.environ.get("REPO_DID") - if not repo: - print("REPO_DID is required", file=sys.stderr) - return 2 - - pds = os.environ.get("PDS_URL", "").rstrip("/") or resolve_pds(repo) - token = get_or_create_auth_token(repo, pds) - if not token: - return 2 - - files = lexicon_files() - if not files: - print("No lexicon schema record files found", file=sys.stderr) - return 2 - - print(f"Publishing {len(files)} lexicons to {repo} via {pds}") - for index, path in enumerate(files, start=1): - nsid = path.stem - record = json.loads(path.read_text(encoding="utf-8")) - put_record(pds, token, repo, LEXICON_COLLECTION, nsid, record) - print(f"{index}/{len(files)} lexicons published... {nsid}", flush=True) - - print("Lexicon publish complete") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main())