efe8e165cf
按 leo 鐵律(2026-06-14)把插件從「直接 SQL 操作基本盤表」改寫成 「只透過基本盤 arcrun/kbdb HTTP API 讀寫」。零建表、零 migration、零 SQL。 - 新增 src/lib/kbdb-client.ts:唯一對外通道,封裝 entries/templates/records API - 新增 src/lib/templates.ts:triplet/entity template 定義(替代建表) - 改寫 21 個違規 action(triplet/graph/entity/search)→ 走 client,圖在插件層記憶體組裝 - 移除所有 migrations、D1/Vectorize/AI 綁定;embedding/語意搜尋歸基本盤 optional 模組 - index.ts 只掛 triplets/graph/entities/search 路由;基本盤路由歸 arcrun/kbdb - 測試改走 mock client(純 node);裁剪 CLAUDE.md 只留 graph 插件 + 鐵律 - 修正 SDD design.md「讀現狀推翻鐵律」的錯誤判斷(共用 D1 → API-as-Wall) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
// 列出所有唯一實體(subject ∪ object)— 零 SQL / 零 D1
|
||
// 取全部 triplet record(走基本盤 API),在記憶體統計 as_subject/as_object/total。
|
||
// GET /entities
|
||
|
||
import type { KbdbClient } from '../lib/kbdb-client';
|
||
import { queryTriplets } from './triplet-crud';
|
||
|
||
export interface EntityStat {
|
||
name: string;
|
||
as_subject: number;
|
||
as_object: number;
|
||
total: number;
|
||
}
|
||
|
||
export interface EntityListResult {
|
||
entities: EntityStat[];
|
||
total: number;
|
||
limit: number;
|
||
offset: number;
|
||
}
|
||
|
||
export async function listTripletEntities(
|
||
client: KbdbClient,
|
||
{ limit = 200, offset = 0, q }: { limit?: number; offset?: number; q?: string },
|
||
): Promise<EntityListResult> {
|
||
const safeLimit = Math.min(limit, 500);
|
||
const { triplets } = await queryTriplets(client, { limit: 2000 });
|
||
|
||
const stats = new Map<string, EntityStat>();
|
||
const bump = (name: string, role: 'as_subject' | 'as_object') => {
|
||
if (!name) return;
|
||
let s = stats.get(name);
|
||
if (!s) {
|
||
s = { name, as_subject: 0, as_object: 0, total: 0 };
|
||
stats.set(name, s);
|
||
}
|
||
s[role] += 1;
|
||
s.total += 1;
|
||
};
|
||
|
||
for (const t of triplets) {
|
||
bump(t.subject, 'as_subject');
|
||
bump(t.object, 'as_object');
|
||
}
|
||
|
||
let entities = [...stats.values()];
|
||
if (q) {
|
||
const needle = q.toLowerCase();
|
||
entities = entities.filter((e) => e.name.toLowerCase().includes(needle));
|
||
}
|
||
entities.sort((a, b) => b.total - a.total);
|
||
|
||
const total = entities.length;
|
||
const page = entities.slice(offset, offset + safeLimit);
|
||
|
||
return { entities: page, total, limit: safeLimit, offset };
|
||
}
|