# ailog ## 概要 atprotoベースの静的ブログジェネレーター。markdownファイルをatproto recordとして保存し、それを元に静的サイトを生成する。 ## Collection Schema ### ai.syui.log.post (ブログ記事) ```json { "lexicon": 1, "id": "ai.syui.log.post", "defs": { "main": { "type": "record", "description": "Record containing a blog post.", "key": "tid", "record": { "type": "object", "required": ["title", "content", "createdAt"], "properties": { "title": { "type": "string", "maxLength": 3000, "maxGraphemes": 300, "description": "The title of the post." }, "content": { "type": "string", "maxLength": 1000000, "maxGraphemes": 100000, "description": "The content of the post." }, "createdAt": { "type": "string", "format": "datetime", "description": "Client-declared timestamp when this post was originally created." } } } } } } ``` ### ai.syui.log.comment (コメント) ```json { "lexicon": 1, "id": "ai.syui.log.comment", "defs": { "main": { "type": "record", "description": "Record containing a comment.", "key": "tid", "record": { "type": "object", "required": ["content", "createdAt", "post"], "properties": { "content": { "type": "string", "maxLength": 100000, "maxGraphemes": 10000, "description": "The content of the comment." }, "createdAt": { "type": "string", "format": "datetime", "description": "Client-declared timestamp when this comment was originally created." }, "parent": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "post": { "type": "ref", "ref": "com.atproto.repo.strongRef" } } } } } } ``` ## CLIコマンド仕様 ### login - ログイン ```bash ailog login ${handle} -p ${password} -s ${pds} ``` - handleからDID, PDSを解決 - 認証情報を`~/.config/syui/ai/log/config.json`に保存 ### post - 記事をatprotoに投稿 ```bash ailog post ``` - `./content/post/*.md`を読み込む - frontmatterからtitle, dateなどを抽出 - markdown本文をcontentに設定 - `ai.syui.log.post`としてputRecord - 既存recordがあれば更新、なければ新規作成 ### build - 静的サイト生成 ```bash ailog build ``` - atprotoから`ai.syui.log.post`のrecordを取得 - markdownをHTMLに変換 - 静的ファイルとして`./public`に出力 ## URL構造 ### 記事一覧 ``` localhost/at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/ai.syui.log.post/ ``` - atprotoから全recordを取得 - 一覧表示 ### 個別記事 ``` localhost/.../3mbnbdt4bas2a ``` - atproto URIからrecordを取得 - markdownをレンダリングして表示 ## データフロー ### 記事投稿 ``` ./content/post/*.md ↓ (frontmatter + markdown解析) ailog post ↓ (putRecord) atproto PDS (ai.syui.log.post) ``` ### 静的サイト生成 ``` atproto PDS (ai.syui.log.post) ↓ (listRecords) ailog build ↓ (markdown → HTML) ./public/*.html ``` ## at browser統合 - atproto URIでの記事アクセス - handle解決 → DID → PDS URLの取得 - NetworkConfig (pdsApi, bskyApi, plcApi) の割り当て ```rust match pds { "bsky.social" | "bsky.app" => NetworkConfig { pds_api: format!("https://{}", pds), plc_api: "https://plc.directory".to_string(), bsky_api: "https://public.api.bsky.app".to_string(), web_url: "https://bsky.app".to_string(), }, "syu.is" => NetworkConfig { pds_api: "https://syu.is".to_string(), plc_api: "https://plc.syu.is".to_string(), bsky_api: "https://bsky.syu.is".to_string(), web_url: "https://web.syu.is".to_string(), }, _ => NetworkConfig { pds_api: format!("https://{}", pds), plc_api: "https://plc.directory".to_string(), bsky_api: "https://public.api.bsky.app".to_string(), web_url: "https://bsky.app".to_string(), } } ``` ## 参考実装 - `./repos/log` - 既存のailog実装 (Rust) - `./repos/frontpage` - frontpage (fyi.unravel.frontpage) - `./repos/pfrazee.com` - Paul FrazeeのLeaflet実装 (TypeScript) ## Paul Frazeeの仕組み pfrazee.comは以下の流れでLeafletをblogに統合: 1. **lexicon schema取得** ```bash lex install pub.leaflet.document lex build --out ./util ``` 2. **データ取得** ```typescript // handle → DID → PDS const did = await resolver.handle.resolve('pfrazee.com') const pds = await resolver.did.resolveAtprotoData(did).pds // listRecords const result = await client.list(leaflet.document, { repo: did, limit: 50 }) ``` 3. **画像取得** ```typescript for (const blobRef of enumBlobRefs(leaflet.value)) { const blobRes = await client.getBlob(did, blobRef.ref) await fsp.writeFile(imagePath, blobRes.payload.body) } ``` 4. **静的サイト生成** (Next.js) - JSONからHTMLレンダリング - 型安全なblock rendering ## 既存のai.syui.log形式 現在のchat形式: ```json { "uri": "at://did:plc:6qyecktefllvenje24fcxnie/ai.syui.log/2025-06-14-blog", "value": { "url": "https://syui.ai/posts/2025-06-14-blog", "post": { "url": "https://syui.ai/posts/2025-06-14-blog", "date": "", "slug": "", "tags": [], "title": "syui.ai", "language": "ja" }, "text": "test", "type": "comment", "$type": "ai.syui.log", "author": { "did": "did:plc:6qyecktefllvenje24fcxnie", "avatar": "https://...", "handle": "ai.syui.ai", "displayName": "ai" }, "createdAt": "2025-06-17T06:24:37.386Z" } } ``` ## 設定ファイル ### ~/.config/syui/ai/log/config.json ```json { "pds": "syu.is", "handle": "ai.syui.ai", "did": "did:plc:6qyecktefllvenje24fcxnie", "access_jwt": "...", "refresh_jwt": "..." } ``` ### ~/.config/syui/ai/log/mapping.json ファイル名とatproto recordの紐付け(自動生成): ```json { "2026-01-08-test.md": { "rkey": "3mbnbdt4bas2a", "uri": "at://did:plc:xxx/ai.syui.log.post/3mbnbdt4bas2a", "cid": "bafyrei..." } } ``` **動作:** - すべてのAPI呼び出し前に自動でrefreshSessionを実行してJWTトークンを更新 - `ailog post` - 初回投稿時にTID形式のrkeyが自動生成され、mappingに保存 - `ailog post` - 2回目以降は既存rkeyで更新(putRecord) - `ailog delete` - 全削除時にmappingもクリア ## ディレクトリ構造 ``` ailog/ ├── content/ │ └── post/ │ ├── 2025-01-08-example.md │ └── ... ├── public/ │ ├── index.html │ ├── posts/ │ │ └── 2025-01-08-example.html │ └── at/ │ └── ... ├── lexicons/ │ └── ai/ │ └── syui/ │ └── log/ │ ├── post.json │ └── comment.json └── templates/ ├── index.html ├── post.html └── layout.html ``` ## 開発優先順位 1. CLIコマンド実装 ✅ - [x] collection schema定義 - [x] `ailog login` - handle → DID解決 + JWT保存 - [x] `ailog post` - ./content/post/*.md → putRecord - [x] `ailog build` - listRecords → 静的HTML生成 2. at browser統合 (進行中) - [x] React/TypeScript環境セットアップ - [x] atproto client library (repos/log/pdsベース) - [ ] at browser components - [ ] 静的サイトへの統合 - [ ] ai.syui.log.post 表示スタイル 3. スタイリング - [ ] Tailwind CSS統合 - [ ] repos/log風デザイン ## 実装済み ### Rust CLI (src/) - `src/main.rs` - CLIエントリーポイント - `src/config.rs` - ~/.config/syui/ai/log/config.json管理 - `src/login.rs` - atproto認証 (describeRepo + createSession) - `src/refresh.rs` - セッションリフレッシュ (refreshSession) - `src/post.rs` - markdown投稿 (createRecord/putRecord) - `src/build.rs` - 静的サイト生成 (listRecords + markdown→HTML) - `src/delete.rs` - record削除 (deleteRecord) ### at browser (browser/) - `browser/package.json` - React + TypeScript + Tailwind - 実装予定: repos/log/pdsの実装をベースに作成 ## 使用方法 ```bash # ログイン ailog l ai.syui.ai -p PASSWORD -s syu.is # 記事投稿(初回: 新規作成、2回目以降: 更新) ailog p # サイト生成 ailog b # 全削除 ailog d ``` **エイリアス:** - `l` = `login` - `p` = `post` - `b` = `build` - `d` = `delete`