@undefineds.co/linx 0.3.5 → 0.3.8
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.
- package/README.md +58 -23
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/index.js +336 -162
- package/dist/index.js.map +1 -1
- package/dist/lib/account-session.js +4 -8
- package/dist/lib/account-session.js.map +1 -1
- package/dist/lib/ai-command.js +228 -178
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/auto-mode/archive.js +38 -7
- package/dist/lib/auto-mode/archive.js.map +1 -1
- package/dist/lib/auto-mode/auth.js.map +1 -1
- package/dist/lib/auto-mode/display.js +71 -45
- package/dist/lib/auto-mode/display.js.map +1 -1
- package/dist/lib/auto-mode/format.js +9 -7
- package/dist/lib/auto-mode/format.js.map +1 -1
- package/dist/lib/auto-mode/hooks/claude.js +12 -2
- package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
- package/dist/lib/auto-mode/hooks/codex.js +17 -7
- package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
- package/dist/lib/auto-mode/hooks/index.js +28 -8
- package/dist/lib/auto-mode/hooks/index.js.map +1 -1
- package/dist/lib/auto-mode/pod-ai.js +20 -37
- package/dist/lib/auto-mode/pod-ai.js.map +1 -1
- package/dist/lib/auto-mode/pod-approval.js +124 -195
- package/dist/lib/auto-mode/pod-approval.js.map +1 -1
- package/dist/lib/auto-mode/pod-persistence.js +169 -90
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
- package/dist/lib/auto-mode/runner.js +683 -81
- package/dist/lib/auto-mode/runner.js.map +1 -1
- package/dist/lib/auto-mode/secretary.js +186 -41
- package/dist/lib/auto-mode/secretary.js.map +1 -1
- package/dist/lib/auto-mode-command.js +32 -32
- package/dist/lib/auto-mode-command.js.map +1 -1
- package/dist/lib/chat-api.js +242 -50
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/bridge.js +164 -17
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/credentials-store.js +33 -42
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/linx-cloud-errors.js +61 -0
- package/dist/lib/linx-cloud-errors.js.map +1 -0
- package/dist/lib/linx-tui-contract.js +8 -5
- package/dist/lib/linx-tui-contract.js.map +1 -1
- package/dist/lib/login-command.js +9 -2
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +3 -20
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +143 -17
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js +2 -6
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
- package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
- package/dist/lib/pi-adapter/backend-command.js +2 -0
- package/dist/lib/pi-adapter/backend-command.js.map +1 -0
- package/dist/lib/pi-adapter/backend-credentials.js +80 -0
- package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
- package/dist/lib/pi-adapter/branding.js +246 -108
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/control-state.js +72 -0
- package/dist/lib/pi-adapter/control-state.js.map +1 -0
- package/dist/lib/pi-adapter/interactive.js +2634 -30
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +382 -210
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +531 -64
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +81 -85
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/pod-status-output.js +54 -0
- package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
- package/dist/lib/pi-adapter/runtime.js +458 -228
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session-control.js +509 -0
- package/dist/lib/pi-adapter/session-control.js.map +1 -0
- package/dist/lib/pi-adapter/session.js +35 -22
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +89 -32
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/sync-recovery.js +89 -0
- package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
- package/dist/lib/pi-adapter/web-fetch.js +13 -14
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
- package/dist/lib/pod-chat-store.js +254 -78
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +156 -35
- package/dist/lib/pod-data-session.js.map +1 -1
- package/dist/lib/solid-auth-store.js +27 -0
- package/dist/lib/solid-auth-store.js.map +1 -0
- package/dist/lib/solid-auth.js +2 -4
- package/dist/lib/solid-auth.js.map +1 -1
- package/dist/lib/solid-client-credentials-login.js +100 -0
- package/dist/lib/solid-client-credentials-login.js.map +1 -0
- package/dist/lib/solid-local-store.js +31 -0
- package/dist/lib/solid-local-store.js.map +1 -0
- package/dist/lib/symphony/archive.js +328 -18
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +2222 -0
- package/dist/lib/symphony/pod-projection.js.map +1 -0
- package/dist/lib/symphony-command.js +602 -178
- package/dist/lib/symphony-command.js.map +1 -1
- package/dist/lib/sync-checkpoint-store.js +74 -0
- package/dist/lib/sync-checkpoint-store.js.map +1 -0
- package/dist/skills/symphony/SKILL.md +665 -0
- package/package.json +15 -9
- package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
- package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
- package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
- package/vendor/agent-runtime/dist/auto-mode.js +288 -31
- package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
- package/vendor/agent-runtime/dist/control-plane.js +79 -0
- package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
- package/vendor/agent-runtime/dist/file-sync.js +314 -0
- package/vendor/agent-runtime/dist/index.d.ts +7 -0
- package/vendor/agent-runtime/dist/index.js +7 -0
- package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
- package/vendor/agent-runtime/dist/reconciler.js +361 -0
- package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
- package/vendor/agent-runtime/dist/symphony.js +362 -57
- package/vendor/agent-runtime/dist/sync.d.ts +271 -0
- package/vendor/agent-runtime/dist/sync.js +550 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
- package/vendor/agent-runtime/dist/turn-controller.js +2 -2
- package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
- package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
- package/vendor/agent-runtime/package.json +8 -1
- package/vendor/pi-web-access/CHANGELOG.md +387 -0
- package/vendor/pi-web-access/LICENSE +21 -0
- package/vendor/pi-web-access/README.md +352 -0
- package/vendor/pi-web-access/activity.ts +101 -0
- package/vendor/pi-web-access/banner.png +0 -0
- package/vendor/pi-web-access/chrome-cookies.ts +322 -0
- package/vendor/pi-web-access/code-search.ts +107 -0
- package/vendor/pi-web-access/curator-page.ts +3359 -0
- package/vendor/pi-web-access/curator-server.ts +605 -0
- package/vendor/pi-web-access/exa.ts +520 -0
- package/vendor/pi-web-access/extract.ts +641 -0
- package/vendor/pi-web-access/gemini-api.ts +112 -0
- package/vendor/pi-web-access/gemini-search.ts +361 -0
- package/vendor/pi-web-access/gemini-url-context.ts +126 -0
- package/vendor/pi-web-access/gemini-web-config.ts +52 -0
- package/vendor/pi-web-access/gemini-web.ts +396 -0
- package/vendor/pi-web-access/github-api.ts +196 -0
- package/vendor/pi-web-access/github-extract.ts +634 -0
- package/vendor/pi-web-access/index.ts +2346 -0
- package/vendor/pi-web-access/package.json +45 -0
- package/vendor/pi-web-access/pdf-extract.ts +192 -0
- package/vendor/pi-web-access/perplexity.ts +195 -0
- package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
- package/vendor/pi-web-access/rsc-extract.ts +338 -0
- package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
- package/vendor/pi-web-access/storage.ts +72 -0
- package/vendor/pi-web-access/summary-review.ts +276 -0
- package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
- package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
- package/vendor/pi-web-access/utils.ts +44 -0
- package/vendor/pi-web-access/video-extract.ts +378 -0
- package/vendor/pi-web-access/youtube-extract.ts +310 -0
- package/dist/lib/pi-adapter/auth.js +0 -68
- package/dist/lib/pi-adapter/auth.js.map +0 -1
- package/dist/lib/pi-adapter/pod-tools.js +0 -140
- package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
- package/dist/skills/drizzle-solid/SKILL.md +0 -340
- package/dist/skills/pod-storage/SKILL.md +0 -100
- package/dist/skills/solid-modeling/SKILL.md +0 -274
- 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.
|