382 lines
8.7 KiB
Markdown
382 lines
8.7 KiB
Markdown
# 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`
|