@undefineds.co/linx 0.3.5 → 0.3.7

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 (172) hide show
  1. package/README.md +58 -23
  2. package/dist/generated/version.js +1 -1
  3. package/dist/generated/version.js.map +1 -1
  4. package/dist/index.js +334 -162
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/account-session.js +4 -8
  7. package/dist/lib/account-session.js.map +1 -1
  8. package/dist/lib/ai-command.js +228 -178
  9. package/dist/lib/ai-command.js.map +1 -1
  10. package/dist/lib/auto-mode/archive.js +38 -7
  11. package/dist/lib/auto-mode/archive.js.map +1 -1
  12. package/dist/lib/auto-mode/auth.js.map +1 -1
  13. package/dist/lib/auto-mode/display.js +71 -45
  14. package/dist/lib/auto-mode/display.js.map +1 -1
  15. package/dist/lib/auto-mode/format.js +9 -7
  16. package/dist/lib/auto-mode/format.js.map +1 -1
  17. package/dist/lib/auto-mode/hooks/claude.js +12 -2
  18. package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
  19. package/dist/lib/auto-mode/hooks/codex.js +17 -7
  20. package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
  21. package/dist/lib/auto-mode/hooks/index.js +28 -8
  22. package/dist/lib/auto-mode/hooks/index.js.map +1 -1
  23. package/dist/lib/auto-mode/pod-ai.js +20 -37
  24. package/dist/lib/auto-mode/pod-ai.js.map +1 -1
  25. package/dist/lib/auto-mode/pod-approval.js +124 -195
  26. package/dist/lib/auto-mode/pod-approval.js.map +1 -1
  27. package/dist/lib/auto-mode/pod-persistence.js +169 -90
  28. package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
  29. package/dist/lib/auto-mode/runner.js +683 -81
  30. package/dist/lib/auto-mode/runner.js.map +1 -1
  31. package/dist/lib/auto-mode/secretary.js +186 -41
  32. package/dist/lib/auto-mode/secretary.js.map +1 -1
  33. package/dist/lib/auto-mode-command.js +32 -32
  34. package/dist/lib/auto-mode-command.js.map +1 -1
  35. package/dist/lib/chat-api.js +242 -50
  36. package/dist/lib/chat-api.js.map +1 -1
  37. package/dist/lib/codex-plugin/bridge.js +164 -17
  38. package/dist/lib/codex-plugin/bridge.js.map +1 -1
  39. package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
  40. package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
  41. package/dist/lib/credentials-store.js +33 -42
  42. package/dist/lib/credentials-store.js.map +1 -1
  43. package/dist/lib/linx-cloud-errors.js +61 -0
  44. package/dist/lib/linx-cloud-errors.js.map +1 -0
  45. package/dist/lib/linx-tui-contract.js +8 -5
  46. package/dist/lib/linx-tui-contract.js.map +1 -1
  47. package/dist/lib/login-command.js +9 -2
  48. package/dist/lib/login-command.js.map +1 -1
  49. package/dist/lib/models.js +3 -20
  50. package/dist/lib/models.js.map +1 -1
  51. package/dist/lib/oidc-auth.js +143 -17
  52. package/dist/lib/oidc-auth.js.map +1 -1
  53. package/dist/lib/oidc-session-storage.js +2 -6
  54. package/dist/lib/oidc-session-storage.js.map +1 -1
  55. package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
  56. package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
  57. package/dist/lib/pi-adapter/backend-command.js +2 -0
  58. package/dist/lib/pi-adapter/backend-command.js.map +1 -0
  59. package/dist/lib/pi-adapter/backend-credentials.js +80 -0
  60. package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
  61. package/dist/lib/pi-adapter/branding.js +246 -108
  62. package/dist/lib/pi-adapter/branding.js.map +1 -1
  63. package/dist/lib/pi-adapter/control-state.js +72 -0
  64. package/dist/lib/pi-adapter/control-state.js.map +1 -0
  65. package/dist/lib/pi-adapter/interactive.js +2634 -30
  66. package/dist/lib/pi-adapter/interactive.js.map +1 -1
  67. package/dist/lib/pi-adapter/pod-approval.js +382 -210
  68. package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
  69. package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
  70. package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
  71. package/dist/lib/pi-adapter/pod-mirror.js +531 -64
  72. package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
  73. package/dist/lib/pi-adapter/pod-native.js +81 -85
  74. package/dist/lib/pi-adapter/pod-native.js.map +1 -1
  75. package/dist/lib/pi-adapter/pod-status-output.js +54 -0
  76. package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
  77. package/dist/lib/pi-adapter/runtime.js +458 -228
  78. package/dist/lib/pi-adapter/runtime.js.map +1 -1
  79. package/dist/lib/pi-adapter/session-control.js +509 -0
  80. package/dist/lib/pi-adapter/session-control.js.map +1 -0
  81. package/dist/lib/pi-adapter/session.js +35 -22
  82. package/dist/lib/pi-adapter/session.js.map +1 -1
  83. package/dist/lib/pi-adapter/stream.js +89 -32
  84. package/dist/lib/pi-adapter/stream.js.map +1 -1
  85. package/dist/lib/pi-adapter/sync-recovery.js +89 -0
  86. package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
  87. package/dist/lib/pi-adapter/web-fetch.js +13 -14
  88. package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
  89. package/dist/lib/pod-chat-store.js +254 -78
  90. package/dist/lib/pod-chat-store.js.map +1 -1
  91. package/dist/lib/pod-data-session.js +156 -35
  92. package/dist/lib/pod-data-session.js.map +1 -1
  93. package/dist/lib/solid-auth-store.js +27 -0
  94. package/dist/lib/solid-auth-store.js.map +1 -0
  95. package/dist/lib/solid-auth.js +2 -4
  96. package/dist/lib/solid-auth.js.map +1 -1
  97. package/dist/lib/solid-client-credentials-login.js +100 -0
  98. package/dist/lib/solid-client-credentials-login.js.map +1 -0
  99. package/dist/lib/solid-local-store.js +31 -0
  100. package/dist/lib/solid-local-store.js.map +1 -0
  101. package/dist/lib/symphony/archive.js +328 -18
  102. package/dist/lib/symphony/archive.js.map +1 -1
  103. package/dist/lib/symphony/pod-projection.js +2222 -0
  104. package/dist/lib/symphony/pod-projection.js.map +1 -0
  105. package/dist/lib/symphony-command.js +602 -178
  106. package/dist/lib/symphony-command.js.map +1 -1
  107. package/dist/lib/sync-checkpoint-store.js +74 -0
  108. package/dist/lib/sync-checkpoint-store.js.map +1 -0
  109. package/dist/skills/symphony/SKILL.md +665 -0
  110. package/package.json +15 -9
  111. package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
  112. package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
  113. package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
  114. package/vendor/agent-runtime/dist/auto-mode.js +288 -31
  115. package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
  116. package/vendor/agent-runtime/dist/control-plane.js +79 -0
  117. package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
  118. package/vendor/agent-runtime/dist/file-sync.js +314 -0
  119. package/vendor/agent-runtime/dist/index.d.ts +7 -0
  120. package/vendor/agent-runtime/dist/index.js +7 -0
  121. package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
  122. package/vendor/agent-runtime/dist/reconciler.js +361 -0
  123. package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
  124. package/vendor/agent-runtime/dist/symphony.js +362 -57
  125. package/vendor/agent-runtime/dist/sync.d.ts +271 -0
  126. package/vendor/agent-runtime/dist/sync.js +550 -0
  127. package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
  128. package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
  129. package/vendor/agent-runtime/dist/turn-controller.js +2 -2
  130. package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
  131. package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
  132. package/vendor/agent-runtime/package.json +8 -1
  133. package/vendor/pi-web-access/CHANGELOG.md +387 -0
  134. package/vendor/pi-web-access/LICENSE +21 -0
  135. package/vendor/pi-web-access/README.md +352 -0
  136. package/vendor/pi-web-access/activity.ts +101 -0
  137. package/vendor/pi-web-access/banner.png +0 -0
  138. package/vendor/pi-web-access/chrome-cookies.ts +322 -0
  139. package/vendor/pi-web-access/code-search.ts +107 -0
  140. package/vendor/pi-web-access/curator-page.ts +3359 -0
  141. package/vendor/pi-web-access/curator-server.ts +605 -0
  142. package/vendor/pi-web-access/exa.ts +520 -0
  143. package/vendor/pi-web-access/extract.ts +641 -0
  144. package/vendor/pi-web-access/gemini-api.ts +112 -0
  145. package/vendor/pi-web-access/gemini-search.ts +361 -0
  146. package/vendor/pi-web-access/gemini-url-context.ts +126 -0
  147. package/vendor/pi-web-access/gemini-web-config.ts +52 -0
  148. package/vendor/pi-web-access/gemini-web.ts +396 -0
  149. package/vendor/pi-web-access/github-api.ts +196 -0
  150. package/vendor/pi-web-access/github-extract.ts +634 -0
  151. package/vendor/pi-web-access/index.ts +2346 -0
  152. package/vendor/pi-web-access/package.json +45 -0
  153. package/vendor/pi-web-access/pdf-extract.ts +192 -0
  154. package/vendor/pi-web-access/perplexity.ts +195 -0
  155. package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
  156. package/vendor/pi-web-access/rsc-extract.ts +338 -0
  157. package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
  158. package/vendor/pi-web-access/storage.ts +72 -0
  159. package/vendor/pi-web-access/summary-review.ts +276 -0
  160. package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
  161. package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
  162. package/vendor/pi-web-access/utils.ts +44 -0
  163. package/vendor/pi-web-access/video-extract.ts +378 -0
  164. package/vendor/pi-web-access/youtube-extract.ts +310 -0
  165. package/dist/lib/pi-adapter/auth.js +0 -68
  166. package/dist/lib/pi-adapter/auth.js.map +0 -1
  167. package/dist/lib/pi-adapter/pod-tools.js +0 -140
  168. package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
  169. package/dist/skills/drizzle-solid/SKILL.md +0 -340
  170. package/dist/skills/pod-storage/SKILL.md +0 -100
  171. package/dist/skills/solid-modeling/SKILL.md +0 -274
  172. package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
@@ -1,68 +0,0 @@
1
- import { getClientCredentialId, getClientCredentialKey, getClientCredentials, loadCredentials, } from '../credentials-store.js';
2
- import { getOidcAccessToken, isOidcLoginExpiredError } from '../oidc-auth.js';
3
- import { getAccessToken } from '../solid-auth.js';
4
- function isRuntimeOverride(value) {
5
- return typeof value === 'object'
6
- && value !== null
7
- && ('loadCredentials' in value
8
- || 'getClientCredentials' in value
9
- || 'getAccessToken' in value
10
- || 'getOidcAccessToken' in value);
11
- }
12
- function resolveOidcExpiresAt(credentials) {
13
- const expiresAt = 'oidcExpiresAt' in credentials.secrets
14
- ? new Date(credentials.secrets.oidcExpiresAt).getTime()
15
- : NaN;
16
- return Number.isFinite(expiresAt) ? expiresAt : Date.now();
17
- }
18
- export async function resolveLinxPiCloudOAuthCredential(issuerUrl, optionsOrRuntime = {}, runtimeArg) {
19
- const defaultRuntime = {
20
- loadCredentials,
21
- getClientCredentials,
22
- getAccessToken,
23
- getOidcAccessToken,
24
- };
25
- const options = isRuntimeOverride(optionsOrRuntime)
26
- ? {}
27
- : optionsOrRuntime;
28
- const runtime = {
29
- ...defaultRuntime,
30
- ...(isRuntimeOverride(optionsOrRuntime) ? optionsOrRuntime : runtimeArg),
31
- };
32
- const stored = runtime.loadCredentials();
33
- if (!stored) {
34
- return null;
35
- }
36
- const clientCredentials = runtime.getClientCredentials(stored);
37
- if (!clientCredentials) {
38
- const oidcAccessToken = await runtime.getOidcAccessToken(stored, { forceRefresh: options.forceRefresh }).catch((error) => {
39
- if (isOidcLoginExpiredError(error)) {
40
- throw error;
41
- }
42
- return null;
43
- });
44
- if (!oidcAccessToken) {
45
- return null;
46
- }
47
- return {
48
- type: 'oauth',
49
- refresh: 'linx-oidc-refresh',
50
- access: oidcAccessToken,
51
- expires: resolveOidcExpiresAt(stored),
52
- };
53
- }
54
- const resolvedIssuerUrl = issuerUrl?.trim() || stored.url;
55
- const clientId = getClientCredentialId(clientCredentials);
56
- const clientSecret = getClientCredentialKey(clientCredentials);
57
- const token = await runtime.getAccessToken(clientId, clientSecret, resolvedIssuerUrl);
58
- if (!token) {
59
- return null;
60
- }
61
- return {
62
- type: 'oauth',
63
- refresh: clientSecret,
64
- access: token.accessToken,
65
- expires: token.expiresAt.getTime(),
66
- };
67
- }
68
- //# sourceMappingURL=auth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/lib/pi-adapter/auth.ts"],"names":[],"mappings":"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"}
@@ -1,140 +0,0 @@
1
- /**
2
- * Pod Read/Write tools — LLM-facing tools for Pod filesystem access.
3
- *
4
- * These tools provide the LLM with the same familiar read/write interface
5
- * as local files, but routed through the authenticated Pod HTTP API.
6
- *
7
- * The LLM should prefer these over local read/write when the path starts
8
- * with a Pod root (e.g., /alice/). For local files, use read/write.
9
- */
10
- import { Type } from '@sinclair/typebox';
11
- import { resolveLinxPodBaseUrl } from '@undefineds.co/models/client';
12
- import { getDefaultPodDataSession } from '../pod-data-session.js';
13
- // ── Parameter Schemas ──────────────────────────────────────────────────────
14
- const PodReadParams = Type.Object({
15
- path: Type.String({ description: 'Pod resource path (e.g., /alice/settings/credentials.ttl)' }),
16
- });
17
- const PodWriteParams = Type.Object({
18
- path: Type.String({ description: 'Pod resource path (e.g., /alice/settings/credentials.ttl)' }),
19
- content: Type.String({ description: 'Content to write' }),
20
- contentType: Type.Optional(Type.String({ description: 'Content-Type header. Default: text/turtle for .ttl, text/markdown for .md, application/json for .json' })),
21
- });
22
- // ── Content-Type inference ──────────────────────────────────────────────────
23
- function inferContentType(path) {
24
- if (path.endsWith('.ttl'))
25
- return 'text/turtle';
26
- if (path.endsWith('.md'))
27
- return 'text/markdown';
28
- if (path.endsWith('.json'))
29
- return 'application/json';
30
- if (path.endsWith('.html'))
31
- return 'text/html';
32
- return 'text/plain';
33
- }
34
- export function resolvePodToolUrl(path, pod) {
35
- if (/^https?:\/\//i.test(path)) {
36
- return path;
37
- }
38
- const webId = pod.webId?.trim() ?? '';
39
- const origin = resolveUrlOrigin(webId) || resolveUrlOrigin(pod.credentials?.url);
40
- if (path.startsWith('/')) {
41
- if (!origin) {
42
- throw new Error('Cannot resolve absolute Pod path without a WebID or issuer URL.');
43
- }
44
- return new URL(path, `${origin}/`).toString();
45
- }
46
- const podBase = webId ? resolveLinxPodBaseUrl(webId) : '';
47
- const baseUrl = podBase || origin;
48
- if (!baseUrl) {
49
- throw new Error('Cannot resolve relative Pod path without a WebID or issuer URL.');
50
- }
51
- return new URL(path, `${baseUrl.replace(/\/+$/, '')}/`).toString();
52
- }
53
- function resolveUrlOrigin(url) {
54
- try {
55
- return typeof url === 'string' && url.trim() ? new URL(url).origin : '';
56
- }
57
- catch {
58
- return '';
59
- }
60
- }
61
- // ── Tool Definitions ────────────────────────────────────────────────────────
62
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
- export const podReadTool = {
64
- name: 'pod_read',
65
- label: 'Pod Read',
66
- description: [
67
- 'Read a file from the user\'s Pod. Use this for any path under the Pod root (e.g., /alice/...).',
68
- 'For local files, use the regular read tool instead.',
69
- ].join('\n'),
70
- parameters: PodReadParams,
71
- async execute(_callId, params) {
72
- return executePodRead(params);
73
- },
74
- };
75
- export async function executePodRead(params, getPodDataSession = getDefaultPodDataSession) {
76
- const path = params.path.trim();
77
- if (!path)
78
- return { content: [{ type: 'text', text: 'Error: path is required' }], isError: true };
79
- const pod = await getPodDataSession();
80
- if (!pod)
81
- return { content: [{ type: 'text', text: 'Error: not connected to Pod' }], isError: true };
82
- try {
83
- const fullUrl = resolvePodToolUrl(path, pod);
84
- const res = await pod.fetch(fullUrl);
85
- if (!res.ok) {
86
- const body = await res.text().catch(() => '');
87
- return { content: [{ type: 'text', text: `Pod read failed: HTTP ${res.status} — ${body.slice(0, 500)}` }], isError: true };
88
- }
89
- const text = await res.text();
90
- return { content: [{ type: 'text', text }] };
91
- }
92
- catch (e) {
93
- return { content: [{ type: 'text', text: `Pod read error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
94
- }
95
- }
96
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
- export const podWriteTool = {
98
- name: 'pod_write',
99
- label: 'Pod Write',
100
- description: [
101
- 'Write content to a file in the user\'s Pod. Use this for any path under the Pod root (e.g., /alice/...).',
102
- 'Content-Type is inferred from the file extension (.ttl → text/turtle, .md → text/markdown, .json → application/json).',
103
- 'For local files, use the regular write tool instead.',
104
- ].join('\n'),
105
- parameters: PodWriteParams,
106
- async execute(_callId, params) {
107
- return executePodWrite(params);
108
- },
109
- };
110
- export async function executePodWrite(params, getPodDataSession = getDefaultPodDataSession) {
111
- const path = params.path.trim();
112
- if (!path)
113
- return { content: [{ type: 'text', text: 'Error: path is required' }], isError: true };
114
- const pod = await getPodDataSession();
115
- if (!pod)
116
- return { content: [{ type: 'text', text: 'Error: not connected to Pod' }], isError: true };
117
- const ct = params.contentType?.trim() || inferContentType(path);
118
- try {
119
- const fullUrl = resolvePodToolUrl(path, pod);
120
- const res = await pod.fetch(fullUrl, {
121
- method: 'PUT',
122
- headers: { 'Content-Type': ct },
123
- body: params.content,
124
- });
125
- if (!res.ok) {
126
- const body = await res.text().catch(() => '');
127
- return { content: [{ type: 'text', text: `Pod write failed: HTTP ${res.status} — ${body.slice(0, 500)}` }], isError: true };
128
- }
129
- return { content: [{ type: 'text', text: `Written: ${path}` }] };
130
- }
131
- catch (e) {
132
- return { content: [{ type: 'text', text: `Pod write error: ${e instanceof Error ? e.message : String(e)}` }], isError: true };
133
- }
134
- }
135
- // ── Pi Extension ────────────────────────────────────────────────────────────
136
- export default function (pi) {
137
- pi.registerTool(podReadTool);
138
- pi.registerTool(podWriteTool);
139
- }
140
- //# sourceMappingURL=pod-tools.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pod-tools.js","sourceRoot":"","sources":["../../../src/lib/pi-adapter/pod-tools.ts"],"names":[],"mappings":"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"}
@@ -1,340 +0,0 @@
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. 相关代码片段
@@ -1,100 +0,0 @@
1
- ---
2
- name: pod-storage
3
- description: Resolve durable Pod writes through shared descriptors, Consensus, pod_schema, and pod_storage instead of inventing paths or Turtle.
4
- allowed-tools: Read, Write, Edit, Bash, Grep, Glob, pod_read, pod_write
5
- ---
6
-
7
- # Pod Storage
8
-
9
- Use this skill when the user asks AI Secretary or another agent to save
10
- credentials, configuration, grants, preferences, or other durable Pod-backed
11
- data.
12
-
13
- ## Core Rule
14
-
15
- Do not invent Pod paths, RDF predicates, subject templates, or Turtle.
16
-
17
- Durable storage semantics belong to `@undefineds.co/models`:
18
-
19
- - descriptors,
20
- - resource base paths,
21
- - subject templates,
22
- - field predicates,
23
- - validation,
24
- - repository-backed commits.
25
-
26
- ## Workflow
27
-
28
- 1. Restate the storage request in structured fields when possible.
29
- 2. Use the injected `udfs` tool for schema lookup and semantic consensus.
30
- LinX/linx-lite should provide runtime context for remote Consensus; without
31
- that context, `udfs` can still run local deterministic schema commands.
32
- 3. For direct schema lookup, call `udfs schema classes`,
33
- `udfs schema predicates`, or `udfs schema describe`.
34
- 4. For ambiguous natural-language storage requests, call `udfs consensus` with a
35
- JSON input payload.
36
- The future service-backed path should use the Responses protocol
37
- (`/v1/conversations` + `/v1/responses`).
38
- 5. Ask the user any returned clarification questions.
39
- 6. Continue the same Responses conversation through the `conversation_id`
40
- request field.
41
- 7. Build a domain-shaped DTO from the returned `schemaUri` and field mapping.
42
- 8. Optionally call `udfs storage validate --input <json>` before committing.
43
- 9. Persist through the shared models ORM/repository, not through hand-written
44
- Turtle or a resource-specific `udfs` shortcut.
45
- 10. Report the schema URI, resource summary, and follow-up status.
46
-
47
- ## Current MVP Check
48
-
49
- The local verification path is the injected `udfs` tool. Do not call back into
50
- `linx`; `udfs` must also work when injected into non-LinX coding agents:
51
-
52
- ```bash
53
- udfs consensus --input '{"session_id":"sess_123","request":"我要保存这个 Cloudflare token","answers":{"token_type":"tunnel-token"}}' --json
54
- udfs schema classes --uri 'https://vocab.xpod.dev/credential#Credential'
55
- udfs schema predicates --uri 'https://vocab.xpod.dev/credential#Credential'
56
- udfs schema predicates --uri 'https://vocab.xpod.dev/credential#Credential' --field apiKey
57
- ```
58
-
59
- Inside LinX or another coding-agent shell, AI should use the injected `udfs`
60
- tool:
61
-
62
- ```bash
63
- udfs consensus --input '{"session_id":"sess_123","request":"我要保存这个 Cloudflare token","answers":{"token_type":"tunnel-token"}}' --json
64
- udfs schema describe 'https://vocab.xpod.dev/credential#Credential'
65
- udfs schema predicates --uri 'https://vocab.xpod.dev/credential#Credential'
66
- udfs storage validate --input '{"schemaUri":"https://vocab.xpod.dev/credential#Credential","operation":"upsert","match":{"service":"infra","providerId":"cloudflare","secretType":"tunnel-token"},"set":{"label":"Cloudflare Tunnel Token","status":"active"}}'
67
- ```
68
-
69
- Expected outcome:
70
-
71
- - clarification question: API token or tunnel token,
72
- - schema URI: `https://vocab.xpod.dev/credential#Credential`,
73
- - predicate mapping includes `apiKey -> https://vocab.xpod.dev/credential#apiKey`,
74
- - descriptor storage: `/settings/credentials.ttl#{id}`,
75
- - validation plan: `/settings/credentials.ttl#infra-cloudflare-tunnel-token`,
76
- - no commit in `udfs`; actual writes go through shared ORM/repository code.
77
- - remote Consensus, when available, is reached through injected
78
- `UDFS_CONSENSUS_BASE_URL` and `UDFS_CONSENSUS_TOKEN`; do not ask the user for a
79
- command-line API key.
80
-
81
- ## Secretary Rules
82
-
83
- - Never store secrets in free-form notes.
84
- - Never log full secret values.
85
- - Use validation before risky writes, but persist through model-owned
86
- repositories/ORM.
87
- - Treat unmodeled durable data as a model proposal flow, not a permanent
88
- catch-all note.
89
- - User/developer-created descriptors must be validated before activation.
90
-
91
- ## Low-Level Pod Tools
92
-
93
- `pod_read` and `pod_write` are protocol tools, not modeling tools. Use them only
94
- when:
95
-
96
- - no shared business model exists yet,
97
- - the operation is explicitly protocol-level,
98
- - or you are inspecting raw Pod state for debugging.
99
-
100
- If a shared model exists, use model-owned helpers instead of hand-editing Turtle.