@undefineds.co/linx 0.2.15 → 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/generated/version.js +3 -0
  2. package/dist/generated/version.js.map +1 -0
  3. package/dist/index.js +51 -225
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/account-api.js +1 -1
  6. package/dist/lib/account-api.js.map +1 -1
  7. package/dist/lib/account-session.js +1 -1
  8. package/dist/lib/account-session.js.map +1 -1
  9. package/dist/lib/ai-command.js +59 -32
  10. package/dist/lib/ai-command.js.map +1 -1
  11. package/dist/lib/chat-api.js +19 -177
  12. package/dist/lib/chat-api.js.map +1 -1
  13. package/dist/lib/codex-plugin/bridge.js.map +1 -1
  14. package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
  15. package/dist/lib/codex-plugin/index.js.map +1 -1
  16. package/dist/lib/codex-plugin/runner.js.map +1 -1
  17. package/dist/lib/credentials-store.js +1 -7
  18. package/dist/lib/credentials-store.js.map +1 -1
  19. package/dist/lib/default-model.js +0 -1
  20. package/dist/lib/default-model.js.map +1 -1
  21. package/dist/lib/login-command.js +2 -17
  22. package/dist/lib/login-command.js.map +1 -1
  23. package/dist/lib/models.js +27 -2
  24. package/dist/lib/models.js.map +1 -1
  25. package/dist/lib/oidc-auth.js +13 -78
  26. package/dist/lib/oidc-auth.js.map +1 -1
  27. package/dist/lib/oidc-session-storage.js.map +1 -1
  28. package/dist/lib/pi-adapter/auth.js +5 -12
  29. package/dist/lib/pi-adapter/auth.js.map +1 -1
  30. package/dist/lib/pi-adapter/branding.js +75 -553
  31. package/dist/lib/pi-adapter/branding.js.map +1 -1
  32. package/dist/lib/pi-adapter/index.js.map +1 -1
  33. package/dist/lib/pi-adapter/interactive.js +4 -154
  34. package/dist/lib/pi-adapter/interactive.js.map +1 -1
  35. package/dist/lib/pi-adapter/runtime.js +23 -138
  36. package/dist/lib/pi-adapter/runtime.js.map +1 -1
  37. package/dist/lib/pi-adapter/stream.js +4 -154
  38. package/dist/lib/pi-adapter/stream.js.map +1 -1
  39. package/dist/lib/pi-adapter/theme.js.map +1 -1
  40. package/dist/lib/pod-chat-store.js +1 -1
  41. package/dist/lib/pod-chat-store.js.map +1 -1
  42. package/dist/lib/profile-identity.js +60 -16
  43. package/dist/lib/profile-identity.js.map +1 -1
  44. package/dist/lib/prompt.js.map +1 -1
  45. package/dist/lib/runtime-target.js +1 -1
  46. package/dist/lib/runtime-target.js.map +1 -1
  47. package/dist/lib/solid-auth.js.map +1 -1
  48. package/dist/lib/thread-utils.js.map +1 -1
  49. package/dist/lib/watch/archive.js +1 -1
  50. package/dist/lib/watch/archive.js.map +1 -1
  51. package/dist/lib/watch/auth.js +1 -1
  52. package/dist/lib/watch/auth.js.map +1 -1
  53. package/dist/lib/watch/codex-composer.js.map +1 -1
  54. package/dist/lib/watch/codex-footer.js.map +1 -1
  55. package/dist/lib/watch/codex-overlay.js.map +1 -1
  56. package/dist/lib/watch/codex-request-form.js +1 -1
  57. package/dist/lib/watch/codex-request-form.js.map +1 -1
  58. package/dist/lib/watch/codex-request-input.js.map +1 -1
  59. package/dist/lib/watch/display.js.map +1 -1
  60. package/dist/lib/watch/format.js.map +1 -1
  61. package/dist/lib/watch/hooks/claude.js.map +1 -1
  62. package/dist/lib/watch/hooks/codebuddy.js.map +1 -1
  63. package/dist/lib/watch/hooks/codex.js.map +1 -1
  64. package/dist/lib/watch/hooks/index.js.map +1 -1
  65. package/dist/lib/watch/hooks/shared.js +1 -0
  66. package/dist/lib/watch/hooks/shared.js.map +1 -1
  67. package/dist/lib/watch/index.js.map +1 -1
  68. package/dist/lib/watch/pod-ai.js +32 -16
  69. package/dist/lib/watch/pod-ai.js.map +1 -1
  70. package/dist/lib/watch/pod-approval.js +203 -481
  71. package/dist/lib/watch/pod-approval.js.map +1 -1
  72. package/dist/lib/watch/pod-persistence.js +37 -24
  73. package/dist/lib/watch/pod-persistence.js.map +1 -1
  74. package/dist/lib/watch/runner.js +1 -4
  75. package/dist/lib/watch/runner.js.map +1 -1
  76. package/dist/lib/watch/types.js.map +1 -1
  77. package/dist/skills/drizzle-solid/SKILL.md +340 -0
  78. package/dist/skills/pod-storage/SKILL.md +60 -0
  79. package/dist/skills/solid-modeling/SKILL.md +274 -0
  80. package/dist/skills/xpod-componentsjs/SKILL.md +284 -0
  81. package/dist/watch-cli.js.map +1 -1
  82. package/package.json +10 -3
  83. package/vendor/client/dist/client/index.d.ts +118 -0
  84. package/vendor/client/dist/client/index.js +260 -0
  85. package/vendor/client/dist/index.d.ts +1 -0
  86. package/vendor/client/dist/index.js +1 -0
  87. package/vendor/client/dist/watch/index.d.ts +226 -0
  88. package/vendor/client/dist/watch/index.js +1114 -0
  89. package/vendor/client/package.json +9 -0
  90. package/dist/lib/node-warning-filter.js +0 -34
  91. package/dist/lib/node-warning-filter.js.map +0 -1
  92. package/dist/lib/pi-adapter/pod-approval.js +0 -8
  93. package/dist/lib/pi-adapter/pod-approval.js.map +0 -1
  94. package/dist/lib/pi-adapter/pod-mirror-mapping.js +0 -189
  95. package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +0 -1
  96. package/dist/lib/pi-adapter/pod-mirror.js +0 -334
  97. package/dist/lib/pi-adapter/pod-mirror.js.map +0 -1
  98. package/dist/lib/pi-adapter/pod-native.js +0 -477
  99. package/dist/lib/pi-adapter/pod-native.js.map +0 -1
  100. package/dist/lib/pi-adapter/session.js +0 -727
  101. package/dist/lib/pi-adapter/session.js.map +0 -1
  102. package/dist/lib/pod-data-session.js +0 -70
  103. package/dist/lib/pod-data-session.js.map +0 -1
@@ -0,0 +1,340 @@
1
+ ---
2
+ name: drizzle-solid
3
+ description: Drizzle Solid ORM 专家,处理 Pod 数据 CRUD、Schema 定义、查询优化、SPARQL 端点配置等问题
4
+ allowed-tools: Read, Write, Edit, Grep, Glob
5
+ ---
6
+
7
+ # Drizzle Solid ORM 专家
8
+
9
+ 你是 XPod 项目的 Drizzle Solid ORM 专家。帮助设计和实现基于 drizzle-solid 的 Pod 数据访问层。
10
+
11
+ ## 核心概念
12
+
13
+ ### drizzle-solid 是什么
14
+
15
+ drizzle-solid 是一个为 Solid Pod 设计的类型安全 ORM,基于 Drizzle ORM 构建,让你能够像操作传统数据库一样操作 Solid Pod 中的 RDF 数据。
16
+
17
+ ### 服务器支持
18
+
19
+ | 能力 | 原生 CSS | xpod |
20
+ |------|----------|------|
21
+ | **基础 CRUD** | ✅ LDP 模式 | ✅ LDP 模式 |
22
+ | **SPARQL SELECT** | ❌ 不支持(Comunica 客户端执行) | ✅ 服务端索引下推 |
23
+ | **SPARQL UPDATE** | ⚠️ 仅 BGP 写入 | ✅ 完整支持 |
24
+ | **条件查询** (where) | Comunica 读文件到内存 | 索引下推(单 Pod) |
25
+ | **聚合函数** (count/sum/avg) | Comunica 读文件到内存 | 索引下推(单 Pod) |
26
+ | **SPARQL 端点** | ❌ 不支持 | ✅ `/-/sparql` Sidecar |
27
+
28
+ ## Schema 定义
29
+
30
+ ### podTable 配置
31
+
32
+ ```typescript
33
+ import { podTable, string, int, datetime, uri } from '@undefineds.co/drizzle-solid';
34
+
35
+ const userTable = podTable('users', {
36
+ id: string('id').primaryKey(),
37
+ name: string('name'),
38
+ email: string('email'),
39
+ age: int('age'),
40
+ createdAt: datetime('createdAt'),
41
+ }, {
42
+ base: '/data/users/', // 容器或资源路径
43
+ type: 'https://schema.org/Person', // RDF 类型
44
+ namespace: UDFS_NAMESPACE, // 自定义命名空间
45
+ subjectTemplate: '{id}.ttl#this', // subject URI 模板
46
+ sparqlEndpoint: '/data/users/-/sparql', // xpod SPARQL 端点
47
+ typeIndex: 'private', // TypeIndex 注册
48
+ autoRegister: true, // 自动注册到 TypeIndex
49
+ });
50
+ ```
51
+
52
+ ### subjectTemplate 模式
53
+
54
+ #### Document 模式(每条记录独立文件)
55
+
56
+ ```typescript
57
+ // 每个用户一个文件: /users/alice.ttl, /users/bob.ttl
58
+ const userTable = podTable('users', { ... }, {
59
+ base: '/users/',
60
+ subjectTemplate: '{id}.ttl', // 或 '{id}.ttl#this'
61
+ });
62
+
63
+ // 按日期分片: /logs/2026/01/17/log-001.ttl
64
+ const logTable = podTable('logs', { ... }, {
65
+ base: '/logs/',
66
+ subjectTemplate: '{yyyy}/{MM}/{dd}/{id}.ttl',
67
+ });
68
+ ```
69
+
70
+ #### Fragment 模式(多条记录共享文件)
71
+
72
+ ```typescript
73
+ // 所有消息在同一文件: /chat/room.ttl#msg-1, /chat/room.ttl#msg-2
74
+ const messageTable = podTable('messages', { ... }, {
75
+ base: '/chat/room.ttl',
76
+ subjectTemplate: '#{id}', // 强制 fragment 模式
77
+ });
78
+ ```
79
+
80
+ ### 关联表设计(同文件存储)
81
+
82
+ 当需要将关联数据存储在同一文件时:
83
+
84
+ ```typescript
85
+ // Thread 和 Message 存储在同一文件
86
+ const ChatThread = podTable('ChatThread', {
87
+ id: string('id').primaryKey(),
88
+ title: string('title'),
89
+ }, {
90
+ base: '/chat/',
91
+ subjectTemplate: '{id}.ttl#this', // /chat/thread-1.ttl#this
92
+ });
93
+
94
+ const ChatMessage = podTable('ChatMessage', {
95
+ id: string('id').primaryKey(),
96
+ threadId: string('threadId'),
97
+ content: string('content'),
98
+ }, {
99
+ base: '/chat/',
100
+ subjectTemplate: '{threadId}.ttl#{id}', // /chat/thread-1.ttl#msg-1
101
+ });
102
+ ```
103
+
104
+ **注意**:使用 `{threadId}` 等外键变量时,查询需要配合 `sparqlEndpoint` 才能跨文件查询。
105
+
106
+ ## SPARQL 端点配置(xpod 专用)
107
+
108
+ ### Fragment Mode(推荐)
109
+
110
+ LDP 和 SPARQL 完全兼容:
111
+
112
+ ```typescript
113
+ const postsTable = podTable('posts', { ... }, {
114
+ base: '/data/posts.ttl',
115
+ subjectTemplate: '#{id}',
116
+ sparqlEndpoint: '/data/posts.ttl/-/sparql',
117
+ });
118
+ ```
119
+
120
+ ### Document Mode + 容器端点
121
+
122
+ 用于跨文件查询:
123
+
124
+ ```typescript
125
+ const usersTable = podTable('users', { ... }, {
126
+ base: '/data/users/',
127
+ subjectTemplate: '{id}.ttl#this',
128
+ sparqlEndpoint: '/data/users/-/sparql', // 容器级 SPARQL 端点
129
+ });
130
+ ```
131
+
132
+ **重要**:Document Mode 下,SPARQL 写操作与 LDP 文件视图不兼容!
133
+ - SPARQL INSERT 不会创建 LDP 文件
134
+ - SPARQL UPDATE 不会更新 LDP 文件
135
+ - 建议:写操作用 LDP,读操作可用 SPARQL 聚合查询
136
+
137
+ ## 查询操作
138
+
139
+ ### 基础 CRUD
140
+
141
+ ```typescript
142
+ import { drizzle, eq, and, gte } from '@undefineds.co/drizzle-solid';
143
+
144
+ const db = drizzle(session, { schema });
145
+
146
+ // 查询所有
147
+ const users = await db.select().from(userTable);
148
+
149
+ // 条件查询
150
+ const adults = await db.select()
151
+ .from(userTable)
152
+ .where(gte(userTable.age, 18));
153
+
154
+ // 复合条件
155
+ const result = await db.select()
156
+ .from(userTable)
157
+ .where(and(
158
+ eq(userTable.status, 'active'),
159
+ gte(userTable.age, 18)
160
+ ));
161
+
162
+ // 插入
163
+ await db.insert(userTable).values({
164
+ id: 'user-1',
165
+ name: 'Alice',
166
+ email: 'alice@example.com',
167
+ });
168
+
169
+ // 更新
170
+ await db.update(userTable)
171
+ .set({ name: 'Alice Smith' })
172
+ .where(eq(userTable.id, 'user-1'));
173
+
174
+ // 删除
175
+ await db.delete(userTable)
176
+ .where(eq(userTable.id, 'user-1'));
177
+ ```
178
+
179
+ ### Drizzle 风格查询
180
+
181
+ ```typescript
182
+ const db = drizzle(session, { schema });
183
+
184
+ // findMany
185
+ const users = await db.query.users.findMany({
186
+ where: { verified: true },
187
+ orderBy: [{ column: schema.users.name, direction: 'asc' }],
188
+ with: { posts: true }, // 关联查询
189
+ });
190
+
191
+ // findFirst
192
+ const user = await db.query.users.findFirst({
193
+ where: eq(schema.users.id, 'user-1'),
194
+ });
195
+
196
+ // findByIri
197
+ const alice = await db.findByIri(schema.users, 'https://pod.example/data/users.ttl#alice');
198
+ ```
199
+
200
+ ### 聚合查询
201
+
202
+ ```typescript
203
+ import { count, max, sum, avg } from '@undefineds.co/drizzle-solid';
204
+
205
+ const stats = await db
206
+ .select({
207
+ totalUsers: count(),
208
+ oldestAge: max(userTable.age),
209
+ })
210
+ .from(userTable);
211
+ ```
212
+
213
+ ## 初始化与容器创建
214
+
215
+ ```typescript
216
+ // 初始化表(创建容器、资源,注册 TypeIndex)
217
+ await db.init([userTable, postTable]);
218
+
219
+ // 手动确保容器存在
220
+ // drizzle-solid 会自动处理,但如果需要手动控制:
221
+ // 1. 先 HEAD 检查容器是否存在
222
+ // 2. 不存在则 PUT 创建
223
+ ```
224
+
225
+ ## 常见问题
226
+
227
+ ### 1. 查询返回空数组
228
+
229
+ **可能原因**:
230
+ - Document Mode 下没有配置 `sparqlEndpoint`
231
+ - 容器不存在
232
+ - 数据存储在不同文件,但没有使用容器级 SPARQL 端点
233
+
234
+ **解决方案**:
235
+ ```typescript
236
+ // 配置容器级 SPARQL 端点
237
+ const table = podTable('items', { ... }, {
238
+ base: '/data/items/',
239
+ sparqlEndpoint: '/data/items/-/sparql',
240
+ });
241
+ ```
242
+
243
+ ### 2. 写入后查询不到数据
244
+
245
+ **可能原因**:Document Mode 下混用了 SPARQL 写入和 LDP 读取
246
+
247
+ **解决方案**:
248
+ - 使用 Fragment Mode(推荐)
249
+ - 或者统一使用 LDP 操作
250
+
251
+ ### 3. subjectTemplate 中的变量不生效
252
+
253
+ **正确用法**:
254
+ ```typescript
255
+ // 使用 {id} 引用主键
256
+ subjectTemplate: '{id}.ttl#this'
257
+
258
+ // 使用其他字段(如 threadId)
259
+ subjectTemplate: '{threadId}.ttl#{id}'
260
+ ```
261
+
262
+ **注意**:变量必须是 schema 中定义的字段名。
263
+
264
+ ### 4. TypeIndex 注册失败
265
+
266
+ **检查**:
267
+ - 确保 `typeIndex: 'private'` 或 `'public'` 已设置
268
+ - 确保 `autoRegister: true`(默认)
269
+ - 确保用户有权限写入 TypeIndex
270
+
271
+ ## 最佳实践
272
+
273
+ ### 1. 优先使用 Fragment Mode
274
+
275
+ ```typescript
276
+ // 推荐:所有数据在一个文件
277
+ const table = podTable('items', { ... }, {
278
+ base: '/data/items.ttl',
279
+ subjectTemplate: '#{id}',
280
+ sparqlEndpoint: '/data/items.ttl/-/sparql',
281
+ });
282
+ ```
283
+
284
+ ### 2. 需要文件隔离时使用 Document Mode + SPARQL 端点
285
+
286
+ ```typescript
287
+ // 每条记录独立文件,但通过 SPARQL 端点聚合查询
288
+ const table = podTable('items', { ... }, {
289
+ base: '/data/items/',
290
+ subjectTemplate: '{id}.ttl#this',
291
+ sparqlEndpoint: '/data/items/-/sparql',
292
+ });
293
+ ```
294
+
295
+ ### 3. 关联数据存储在同一文件
296
+
297
+ ```typescript
298
+ // Thread 和 Message 在同一文件,便于原子操作
299
+ const ChatThread = podTable('ChatThread', { ... }, {
300
+ base: '/chat/',
301
+ subjectTemplate: '{id}.ttl#this',
302
+ });
303
+
304
+ const ChatMessage = podTable('ChatMessage', { ... }, {
305
+ base: '/chat/',
306
+ subjectTemplate: '{threadId}.ttl#{id}',
307
+ });
308
+ ```
309
+
310
+ ### 4. 使用 namespace 统一管理自定义谓词
311
+
312
+ ```typescript
313
+ import { UDFS_NAMESPACE } from '@/vocab';
314
+
315
+ const table = podTable('items', {
316
+ status: string('status'), // 映射到 udfs:status
317
+ }, {
318
+ namespace: UDFS_NAMESPACE,
319
+ });
320
+ ```
321
+
322
+ ## 参考资料
323
+
324
+ - drizzle-solid README: `node_modules/@undefineds.co/drizzle-solid/README.md`
325
+ - 项目 vocab 定义: `src/vocab/`
326
+ - 现有 schema 示例: `src/api/chatkit/schema.ts`, `src/credential/schema/tables.ts`
327
+
328
+ ## 问题反馈
329
+
330
+ 如果发现 drizzle-solid 设计不合理或存在 bug,通过 git MCP 向 drizzle-solid 仓库提 issue:
331
+
332
+ ```
333
+ 仓库: undefinedsco/drizzle-solid
334
+ ```
335
+
336
+ 提 issue 时请包含:
337
+ 1. 问题描述
338
+ 2. 复现步骤
339
+ 3. 期望行为 vs 实际行为
340
+ 4. 相关代码片段
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: pod-storage
3
+ description: Pod 文件系统操作 — 使用 pod_read/pod_write 读写用户 Pod 中的数据。
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob, pod_read, pod_write
5
+ ---
6
+
7
+ # Pod 文件系统
8
+
9
+ 用户的数据存储在 Pod 中。访问 Pod 文件使用 `pod_read` / `pod_write`(不是本地 read/write)。
10
+
11
+ ## 路径约定
12
+
13
+ ```
14
+ /alice/
15
+ ├── settings/ # 配置和凭据
16
+ │ ├── credentials.ttl # 第三方 API key
17
+ │ └── preferences.ttl # 用户偏好
18
+ ├── logs/ # 工具/Agent 输出日志
19
+ │ └── tasks/ # 按任务组织
20
+ ├── repos/ # git 仓库工作区
21
+ └── data/ # 用户自己的数据
22
+ ```
23
+
24
+ ## 读写
25
+
26
+ ```
27
+ pod_read /alice/settings/credentials.ttl
28
+ pod_write /alice/settings/credentials.ttl "content"
29
+ ```
30
+
31
+ 用 `pod_read` 读 Pod 文件,`pod_write` 写 Pod 文件。Content-Type 由扩展名自动推断(.ttl → text/turtle)。
32
+
33
+ ## 凭据约定
34
+
35
+ 第三方 API key 存在 `/alice/settings/credentials.ttl`,每条一个节点:
36
+
37
+ ```turtle
38
+ @prefix xpod: <https://undefineds.co/xpod#> .
39
+
40
+ <#jina> xpod:apiKey "jina_xxx" .
41
+ <#openai> xpod:apiKey "sk-xxx" .
42
+ ```
43
+
44
+ 需要 key 时先用 `pod_read` 读这个文件。没有就引导用户去对应网站注册,拿到后用 `pod_write` 写入。
45
+
46
+ 写入时注意保留已有内容,只修改目标凭据行。
47
+
48
+ ## 查找
49
+
50
+ 不知道文件在哪时用 `bash` 配合 Pod HTTP API,或用 Grep 搜索本地 Pod mirror。
51
+
52
+ ## 日志
53
+
54
+ 工具执行结果写入 `/alice/logs/`,按任务 ID 分目录:
55
+
56
+ ```
57
+ /alice/logs/tasks/task-001/
58
+ ├── stdout.log
59
+ └── result.json
60
+ ```
@@ -0,0 +1,274 @@
1
+ ---
2
+ name: solid-modeling
3
+ description: Solid/RDF 数据建模专家,处理 Pod 数据结构设计、类继承、属性定义、命名空间等问题
4
+ allowed-tools: Read, Write, Edit, Grep, Glob
5
+ ---
6
+
7
+ # Solid/RDF 数据建模专家
8
+
9
+ 你是 XPod 项目的 Solid/RDF 数据建模专家。帮助设计符合 Solid 规范和 RDF 最佳实践的数据模型。
10
+
11
+ ## 核心原则
12
+
13
+ ### 数据主权
14
+
15
+ 用户数据存储在用户自己的 Pod 中,服务器不存储用户数据。
16
+
17
+ ### 标准词汇表优先
18
+
19
+ 优先复用已有的标准词汇表,只在必要时定义自定义词汇。
20
+
21
+ | 用途 | 词汇表 | 前缀 | 导入 |
22
+ |------|--------|------|------|
23
+ | 自定义 | Undefineds Namespace | `udfs:` | `import { UDFS } from '@/vocab'` |
24
+ | RDF 基础 | RDF/RDFS | `rdf:`, `rdfs:` | `import { RDF, RDFS } from '@/vocab'` |
25
+ | 时间/元数据 | Dublin Core | `dc:` | `import { DCTerms } from '@/vocab'` |
26
+ | 容器/资源 | LDP | `ldp:` | `import { LDP } from '@/vocab'` |
27
+ | 个人信息 | FOAF | `foaf:` | `import { FOAF } from '@/vocab'` |
28
+ | 访问控制 | ACL | `acl:` | `import { ACL } from '@/vocab'` |
29
+ | 数据类型 | XSD | `xsd:` | `import { XSD } from '@/vocab'` |
30
+
31
+ ## 命名规范
32
+
33
+ ### 词汇表命名
34
+
35
+ | 类型 | 格式 | 示例 |
36
+ |------|------|------|
37
+ | **Class** | PascalCase (大写开头) | `Credential`, `Provider`, `Model` |
38
+ | **Property** | camelCase (小写开头) | `apiKey`, `baseUrl`, `createdAt` |
39
+ | **实例 ID** | kebab-case | `#my-entity`, `#instance-001` |
40
+
41
+ ### 使用 Vocab 定义
42
+
43
+ 项目使用 `src/vocab/` 统一管理词汇表:
44
+
45
+ ```typescript
46
+ // src/vocab/udfs.ts - UDFS 词汇表
47
+ export const UDFS = createNamespace('udfs', 'https://undefineds.co/ns#', {
48
+ // Classes (大写)
49
+ Credential: 'Credential',
50
+ Provider: 'Provider',
51
+ Model: 'Model',
52
+
53
+ // Properties (小写)
54
+ apiKey: 'apiKey',
55
+ baseUrl: 'baseUrl',
56
+ status: 'status',
57
+ });
58
+ ```
59
+
60
+ **使用方式**:
61
+
62
+ ```typescript
63
+ import { UDFS, UDFS_NAMESPACE } from '@/vocab';
64
+
65
+ // 使用 Class
66
+ const type = UDFS.Credential; // 'https://undefineds.co/ns#Credential'
67
+
68
+ // 使用 Property
69
+ const prop = UDFS.apiKey; // 'https://undefineds.co/ns#apiKey'
70
+
71
+ // 动态构建 URI
72
+ const custom = UDFS('CustomTerm'); // 'https://undefineds.co/ns#CustomTerm'
73
+ ```
74
+
75
+ ## drizzle-solid Schema 定义
76
+
77
+ 项目级业务语义不要写进这个 skill 文件。
78
+
79
+ - 这类定义应放在仓库的 models/schema/docs 里,由代码和 shared docs 作为单一真相。
80
+ - 这个 skill 只保留通用 Solid/RDF 建模原则、drizzle-solid 约束和可复用的模式。
81
+ - 如果某个产品需要定义 `chat` / `thread` / `session` 的具体含义,应写回对应 package 的 schema 注释和 shared docs,而不是放到 skill。
82
+
83
+ ### 基本结构
84
+
85
+ ```typescript
86
+ import { podTable, string, uri, datetime, int } from 'drizzle-solid';
87
+ import { UDFS, UDFS_NAMESPACE } from '../vocab';
88
+
89
+ /**
90
+ * Credential - 凭据
91
+ *
92
+ * 存储位置: /settings/credentials.ttl
93
+ */
94
+ export const Credential = podTable(
95
+ 'Credential', // 表名用 PascalCase
96
+ {
97
+ id: string('id').primaryKey(),
98
+ provider: uri('provider'),
99
+ apiKey: string('apiKey'),
100
+ status: string('status'),
101
+ createdAt: datetime('createdAt'),
102
+ },
103
+ {
104
+ base: '/settings/credentials.ttl',
105
+ type: UDFS.Credential, // 使用 vocab 而不是硬编码字符串
106
+ namespace: UDFS_NAMESPACE,
107
+ subjectTemplate: '#{id}',
108
+ },
109
+ );
110
+ ```
111
+
112
+ ### 关系定义
113
+
114
+ ```typescript
115
+ import { relations } from 'drizzle-solid';
116
+
117
+ export const CredentialRelations = relations(Credential, ({ one }) => ({
118
+ provider: one(Provider, {
119
+ fields: [Credential.provider],
120
+ references: [Provider.id],
121
+ }),
122
+ }));
123
+ ```
124
+
125
+ ## 类设计
126
+
127
+ ### 使用 Class 继承表达用途分类
128
+
129
+ 当实体有共同特征但不同用途时,使用 `rdfs:subClassOf`:
130
+
131
+ ```turtle
132
+ # 基类
133
+ udfs:Provider a rdfs:Class ;
134
+ rdfs:label "Provider" ;
135
+ rdfs:comment "服务供应商基类" .
136
+
137
+ # 子类 - 按用途区分
138
+ udfs:AgentProvider rdfs:subClassOf udfs:Provider ;
139
+ rdfs:label "Agent Provider" .
140
+ ```
141
+
142
+ ### 用属性区分实现细节
143
+
144
+ 具体实现方式用属性表达,不用子类:
145
+
146
+ ```turtle
147
+ # 正确:用属性区分实现类型
148
+ <#provider-a> a udfs:Provider ;
149
+ udfs:executorType "claude" .
150
+
151
+ <#provider-b> a udfs:Provider ;
152
+ udfs:executorType "openai" .
153
+
154
+ # 错误:不要为每种实现创建子类
155
+ # udfs:ClaudeProvider rdfs:subClassOf udfs:Provider . ❌
156
+ ```
157
+
158
+ **规则**:
159
+ - Class 继承区分**用途/功能**
160
+ - 属性区分**具体实现**
161
+
162
+ ### 定义与实例分离
163
+
164
+ 静态定义(模板)和运行时实例分开建模:
165
+
166
+ ```turtle
167
+ # 定义(模板) - 静态配置,描述"是什么"
168
+ <#agent-config> a udfs:AgentConfig ;
169
+ udfs:displayName "Indexing Agent" ;
170
+ udfs:systemPrompt "..." .
171
+
172
+ # 实例 - 运行时状态,描述"正在做什么"
173
+ <#agent-status> a udfs:AgentStatus ;
174
+ udfs:agentId "indexing" ;
175
+ udfs:status "running" ;
176
+ udfs:currentTaskId "task-123" .
177
+ ```
178
+
179
+ ## 属性设计
180
+
181
+ ### 使用 URI 引用关联实体
182
+
183
+ 实体间关系用 URI 引用,不用字符串:
184
+
185
+ ```turtle
186
+ # 正确:URI 引用
187
+ <#credential> a udfs:Credential ;
188
+ udfs:provider </settings/ai/providers.ttl#google> .
189
+
190
+ # 错误:字符串值
191
+ <#credential> a udfs:Credential ;
192
+ udfs:provider "google" . ❌
193
+ ```
194
+
195
+ ### 时间字段统一用 datetime
196
+
197
+ ```typescript
198
+ // 正确
199
+ createdAt: datetime('createdAt'),
200
+ updatedAt: datetime('updatedAt'),
201
+
202
+ // 错误 - 不要用 string 存时间
203
+ startedAt: string('startedAt'), // ❌
204
+ ```
205
+
206
+ ### 布尔值
207
+
208
+ drizzle-solid 目前用 string 存储布尔值:
209
+
210
+ ```typescript
211
+ enabled: string('enabled'), // 存储 "true" / "false"
212
+ ```
213
+
214
+ 代码中需要手动比较:`enabled === 'true'`
215
+
216
+ ## 文件组织
217
+
218
+ ### 按功能分文件
219
+
220
+ ```
221
+ pod:/settings/
222
+ ├── ai/
223
+ │ ├── providers.ttl # AI 供应商
224
+ │ ├── models.ttl # AI 模型
225
+ │ ├── agent-providers.ttl # Agent 供应商
226
+ │ ├── agents.ttl # Agent 配置
227
+ │ ├── agent-status.ttl # Agent 状态
228
+ │ ├── config.ttl # Pod 级 AI 配置
229
+ │ ├── vector-stores.ttl # 向量知识库
230
+ │ └── indexed-files.ttl # 已索引文件
231
+ ├── credentials.ttl # 凭据(敏感信息单独存放)
232
+ └── prefs.ttl # 用户偏好设置
233
+ ```
234
+
235
+ ### 文件引用规则
236
+
237
+ 同文件用 `#fragment`,跨文件用完整路径:
238
+
239
+ ```turtle
240
+ # 同文件引用
241
+ <#entity-a> udfs:relatedTo <#entity-b> .
242
+
243
+ # 跨文件引用
244
+ <#credential> udfs:provider </settings/ai/providers.ttl#google> .
245
+
246
+ # 跨 Pod 引用
247
+ <#entity-a> udfs:relatedTo <https://other.pod/file.ttl#entity-b> .
248
+ ```
249
+
250
+ ## 检查清单
251
+
252
+ 设计新数据模型时:
253
+
254
+ - [ ] 是否有可复用的标准词汇表?
255
+ - [ ] 新词汇是否已添加到 `src/vocab/udfs.ts`?
256
+ - [ ] Class 名是否大写开头?Property 名是否小写开头?
257
+ - [ ] 类继承是否按用途区分(不是按实现)?
258
+ - [ ] 定义和实例是否分离?
259
+ - [ ] 实体关系是否用 URI 引用(不是字符串)?
260
+ - [ ] 时间字段是否用 `datetime()` 类型?
261
+ - [ ] 敏感数据是否单独存放?
262
+ - [ ] Schema 是否使用 `UDFS.ClassName` 而不是硬编码字符串?
263
+
264
+ ## 参考文件
265
+
266
+ - **Vocab 定义**: `src/vocab/udfs.ts`, `src/vocab/external.ts`
267
+ - **Credential Schema**: `src/credential/schema/tables.ts`
268
+ - **Embedding Schema**: `src/embedding/schema/tables.ts`
269
+ - **Agent Schema**: `src/agents/schema/`
270
+ - **Task Schema**: `src/task/schema.ts`
271
+ - **Credential Schema**: `src/credential/schema/tables.ts`
272
+ - **Embedding Schema**: `src/embedding/schema/tables.ts`
273
+ - **Agent Schema**: `src/agents/schema/`
274
+ - **Task Schema**: `src/task/schema.ts`