@undefineds.co/linx 0.2.12 → 0.2.13
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/dist/lib/pi-adapter/pod-mirror.js +18 -47
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +61 -8
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/runtime.js +21 -2
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session.js +163 -49
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +57 -1
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pod-data-session.js +70 -0
- package/dist/lib/pod-data-session.js.map +1 -0
- package/dist/lib/profile-identity.js +14 -58
- package/dist/lib/profile-identity.js.map +1 -1
- package/dist/lib/watch/pod-ai.js +16 -33
- package/dist/lib/watch/pod-ai.js.map +1 -1
- package/dist/lib/watch/pod-approval.js +100 -103
- package/dist/lib/watch/pod-approval.js.map +1 -1
- package/dist/lib/watch/pod-persistence.js +22 -36
- package/dist/lib/watch/pod-persistence.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import { getClientCredentialId, getClientCredentialKey, getClientCredentials, loadCredentials, } from './credentials-store.js';
|
|
2
1
|
import { extractProfileUsernameFromWebId } from '@undefineds.co/models/client';
|
|
3
2
|
import { resolveSolidProfileIdentityFromWebIdDocument } from '@undefineds.co/models/profile';
|
|
4
|
-
import {
|
|
5
|
-
import { authenticate } from './solid-auth.js';
|
|
3
|
+
import { getDefaultPodDataSession } from './pod-data-session.js';
|
|
6
4
|
const resourceCache = new Map();
|
|
7
5
|
const defaultResolveProfileIdentity = (session, webId) => {
|
|
8
6
|
return resolveSolidProfileIdentityFromWebIdDocument(session, { webId });
|
|
9
7
|
};
|
|
10
8
|
const defaultRuntime = {
|
|
11
|
-
|
|
12
|
-
getClientCredentials,
|
|
13
|
-
getOidcAccessToken,
|
|
14
|
-
authenticate,
|
|
9
|
+
getPodDataSession: getDefaultPodDataSession,
|
|
15
10
|
resolveProfileIdentity: defaultResolveProfileIdentity,
|
|
16
11
|
getCachedResource(key) {
|
|
17
12
|
return resourceCache.get(key) ?? null;
|
|
@@ -22,11 +17,11 @@ const defaultRuntime = {
|
|
|
22
17
|
};
|
|
23
18
|
export async function resolveProfileDisplayName(options = {}) {
|
|
24
19
|
const runtime = options.runtime ?? defaultRuntime;
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
20
|
+
const session = await runtime.getPodDataSession();
|
|
21
|
+
if (!session) {
|
|
27
22
|
return null;
|
|
28
23
|
}
|
|
29
|
-
return await withTimeout(readProfileDisplayName(
|
|
24
|
+
return await withTimeout(readProfileDisplayName(session, runtime).catch(() => null), options.timeoutMs ?? 5_000);
|
|
30
25
|
}
|
|
31
26
|
export function extractUsernameFromWebId(webId) {
|
|
32
27
|
return extractProfileUsernameFromWebId(webId);
|
|
@@ -34,70 +29,31 @@ export function extractUsernameFromWebId(webId) {
|
|
|
34
29
|
export function clearProfileIdentityResourceCache() {
|
|
35
30
|
resourceCache.clear();
|
|
36
31
|
}
|
|
37
|
-
async function readProfileDisplayName(
|
|
38
|
-
const resource = await getOrCreateProfileResource(
|
|
32
|
+
async function readProfileDisplayName(session, runtime) {
|
|
33
|
+
const resource = await getOrCreateProfileResource(session, runtime);
|
|
39
34
|
return resource.identity?.displayName ?? null;
|
|
40
35
|
}
|
|
41
|
-
async function getOrCreateProfileResource(
|
|
42
|
-
const cacheKey = buildProfileResourceCacheKey(
|
|
36
|
+
async function getOrCreateProfileResource(session, runtime) {
|
|
37
|
+
const cacheKey = buildProfileResourceCacheKey(session);
|
|
43
38
|
const cached = runtime.getCachedResource?.(cacheKey);
|
|
44
39
|
if (cached) {
|
|
45
40
|
return cached;
|
|
46
41
|
}
|
|
47
|
-
const session = await createProfileSession(credentials, runtime);
|
|
48
42
|
const resolveIdentity = runtime.resolveProfileIdentity ?? defaultResolveProfileIdentity;
|
|
49
43
|
const resource = {
|
|
50
44
|
session,
|
|
51
|
-
identity: await resolveIdentity(session,
|
|
45
|
+
identity: await resolveIdentity(session.solidSession ?? session, session.webId),
|
|
52
46
|
};
|
|
53
47
|
runtime.setCachedResource?.(cacheKey, resource);
|
|
54
48
|
return resource;
|
|
55
49
|
}
|
|
56
|
-
|
|
57
|
-
const clientCredentials = runtime.getClientCredentials(credentials);
|
|
58
|
-
if (clientCredentials) {
|
|
59
|
-
const { session } = await runtime.authenticate(getClientCredentialId(clientCredentials), getClientCredentialKey(clientCredentials), credentials.url);
|
|
60
|
-
return session;
|
|
61
|
-
}
|
|
62
|
-
if (credentials.authType === 'oidc_oauth') {
|
|
63
|
-
const accessToken = await runtime.getOidcAccessToken(credentials);
|
|
64
|
-
if (!accessToken) {
|
|
65
|
-
throw new Error('Failed to restore OIDC access token for profile lookup');
|
|
66
|
-
}
|
|
67
|
-
return createOidcSessionLike(credentials, accessToken);
|
|
68
|
-
}
|
|
69
|
-
throw new Error('Unsupported credential type for profile lookup');
|
|
70
|
-
}
|
|
71
|
-
function buildProfileResourceCacheKey(credentials) {
|
|
72
|
-
const clientCredentials = getClientCredentials(credentials);
|
|
73
|
-
const secretVersion = clientCredentials
|
|
74
|
-
? getClientCredentialId(clientCredentials)
|
|
75
|
-
: 'oidcRefreshToken' in credentials.secrets
|
|
76
|
-
? credentials.secrets.oidcRefreshToken
|
|
77
|
-
: '';
|
|
50
|
+
function buildProfileResourceCacheKey(session) {
|
|
78
51
|
return [
|
|
79
|
-
credentials.authType,
|
|
80
|
-
credentials.url,
|
|
81
|
-
|
|
82
|
-
secretVersion,
|
|
52
|
+
session.credentials.authType,
|
|
53
|
+
session.credentials.url,
|
|
54
|
+
session.webId,
|
|
83
55
|
].join('\n');
|
|
84
56
|
}
|
|
85
|
-
function createOidcSessionLike(credentials, accessToken) {
|
|
86
|
-
const podUrl = credentials.webId.replace('/card#me', '').replace(/\/?$/, '/');
|
|
87
|
-
return {
|
|
88
|
-
info: {
|
|
89
|
-
isLoggedIn: true,
|
|
90
|
-
webId: credentials.webId,
|
|
91
|
-
podUrl,
|
|
92
|
-
},
|
|
93
|
-
async logout() { },
|
|
94
|
-
fetch(url, init) {
|
|
95
|
-
const headers = new Headers(init?.headers);
|
|
96
|
-
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
97
|
-
return fetch(url, { ...init, headers });
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
57
|
async function withTimeout(promise, timeoutMs) {
|
|
102
58
|
let timer;
|
|
103
59
|
const timeout = new Promise((resolve) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-identity.js","sourceRoot":"","sources":["../../src/lib/profile-identity.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"profile-identity.js","sourceRoot":"","sources":["../../src/lib/profile-identity.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/watch/pod-ai.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getDefaultPodDataSession } from '../pod-data-session.js';
|
|
2
2
|
async function dynamicImport(specifier) {
|
|
3
3
|
const loader = new Function('modulePath', 'return import(modulePath)');
|
|
4
4
|
return loader(specifier);
|
|
@@ -97,9 +97,6 @@ function buildBackendEnv(match, backend) {
|
|
|
97
97
|
function missingPodClientCredentialsMessage() {
|
|
98
98
|
return 'LinX cloud credential source is not connected yet. Run `linx login` first.';
|
|
99
99
|
}
|
|
100
|
-
function unsupportedStoredAuthMessage() {
|
|
101
|
-
return 'LinX watch cloud credential source requires client credentials auth in `~/.linx`.';
|
|
102
|
-
}
|
|
103
100
|
export function podCredentialMissingMessage(backend) {
|
|
104
101
|
if (backend === 'claude') {
|
|
105
102
|
return 'No active Anthropic AI credential was found in LinX cloud credential config. Configure one in `/settings/credentials.ttl` and try again.';
|
|
@@ -113,17 +110,13 @@ export function podCredentialMissingMessage(backend) {
|
|
|
113
110
|
return 'No matching Pod AI credential was found.';
|
|
114
111
|
}
|
|
115
112
|
async function createDefaultRuntime() {
|
|
116
|
-
const [
|
|
117
|
-
dynamicImport('../credentials-store.js'),
|
|
118
|
-
dynamicImport('../solid-auth.js'),
|
|
113
|
+
const [models] = await Promise.all([
|
|
119
114
|
dynamicImport('../models.js'),
|
|
120
115
|
]);
|
|
121
116
|
return {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
createDb(session) {
|
|
126
|
-
return models.drizzle(session, {
|
|
117
|
+
getPodDataSession: getDefaultPodDataSession,
|
|
118
|
+
createDb(podSession) {
|
|
119
|
+
return models.drizzle(podSession.solidSession ?? podSession, {
|
|
127
120
|
logger: false,
|
|
128
121
|
disableInteropDiscovery: true,
|
|
129
122
|
schema: models.solidSchema,
|
|
@@ -135,30 +128,20 @@ async function createDefaultRuntime() {
|
|
|
135
128
|
}
|
|
136
129
|
export async function loadPodBackendCredential(backend, runtime) {
|
|
137
130
|
const activeRuntime = runtime ?? await createDefaultRuntime();
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
131
|
+
const podSession = await activeRuntime.getPodDataSession();
|
|
132
|
+
if (!podSession) {
|
|
140
133
|
throw new Error(missingPodClientCredentialsMessage());
|
|
141
134
|
}
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
db.select().from(activeRuntime.credentialTable).execute(),
|
|
151
|
-
db.select().from(activeRuntime.aiProviderTable).execute(),
|
|
152
|
-
]);
|
|
153
|
-
const match = selectPodCredentialForBackend(backend, credentials, providers);
|
|
154
|
-
if (!match) {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
return buildBackendEnv(match, backend);
|
|
158
|
-
}
|
|
159
|
-
finally {
|
|
160
|
-
await session.logout().catch(() => undefined);
|
|
135
|
+
const db = activeRuntime.createDb(podSession);
|
|
136
|
+
const [credentials, providers] = await Promise.all([
|
|
137
|
+
db.select().from(activeRuntime.credentialTable).execute(),
|
|
138
|
+
db.select().from(activeRuntime.aiProviderTable).execute(),
|
|
139
|
+
]);
|
|
140
|
+
const match = selectPodCredentialForBackend(backend, credentials, providers);
|
|
141
|
+
if (!match) {
|
|
142
|
+
return null;
|
|
161
143
|
}
|
|
144
|
+
return buildBackendEnv(match, backend);
|
|
162
145
|
}
|
|
163
146
|
export const __podInternal = {
|
|
164
147
|
POD_PROVIDER_IDS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pod-ai.js","sourceRoot":"","sources":["../../../src/lib/watch/pod-ai.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pod-ai.js","sourceRoot":"","sources":["../../../src/lib/watch/pod-ai.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
-
import {
|
|
3
|
-
import { AS_ACTOR, AS_ANNOUNCE, AS_OBJECT, DCT_CREATED, ODRL_ACTION, ODRL_POLICY, ODRL_TARGET, RDF_TYPE, UDFS_ACTION, UDFS_ACTOR, UDFS_ACTOR_ROLE, UDFS_APPROVAL_REQUEST, UDFS_ASSIGNED_TO, UDFS_AUDIT_ENTRY, UDFS_AUTONOMY_GRANT, UDFS_CONTEXT, UDFS_DECISION_BY, UDFS_DECISION_ROLE, UDFS_EFFECT, UDFS_ON_BEHALF_OF, UDFS_POLICY_VERSION, UDFS_REASON, UDFS_RESOLVED_AT, UDFS_REVOKED_AT, UDFS_RISK, UDFS_RISK_CEILING, UDFS_SESSION, UDFS_STATUS, UDFS_TOOL_CALL_ID, UDFS_TOOL_NAME, buildApprovalResourceUrl, buildAuditResourceUrl, buildGrantResourceUrl, buildInboxResourceUrl, firstIri, firstLiteral, iri, listTurtleResources, literal, parseManagedTurtleBlocks, readTurtleResource, subjectIdFromResourceUrl, upsertManagedTurtleBlock, } from '../pi-adapter/pod-native.js';
|
|
2
|
+
import { getDefaultPodDataSession } from '../pod-data-session.js';
|
|
3
|
+
import { AS_ACTOR, AS_ANNOUNCE, AS_OBJECT, buildApprovalDocumentUrl, DCT_CREATED, ODRL_ACTION, ODRL_POLICY, ODRL_TARGET, RDF_TYPE, UDFS_ACTION, UDFS_ACTOR, UDFS_ACTOR_ROLE, UDFS_APPROVAL_REQUEST, UDFS_ASSIGNED_TO, UDFS_AUDIT_ENTRY, UDFS_AUTONOMY_GRANT, UDFS_CONTEXT, UDFS_DECISION_BY, UDFS_DECISION_ROLE, UDFS_EFFECT, UDFS_ON_BEHALF_OF, UDFS_POLICY_VERSION, UDFS_REASON, UDFS_RESOLVED_AT, UDFS_REVOKED_AT, UDFS_RISK, UDFS_RISK_CEILING, UDFS_SESSION, UDFS_STATUS, UDFS_TOOL_CALL_ID, UDFS_TOOL_NAME, buildApprovalResourceUrl, buildAuditDocumentUrl, buildAuditResourceUrl, buildGrantResourceUrl, buildInboxResourceUrl, firstIri, firstLiteral, iri, listTurtleResources, listTurtleResourcesRecursive, literal, parseManagedTurtleBlocks, readTurtleResource, subjectIdFromResourceUrl, upsertManagedTurtleBlock, } from '../pi-adapter/pod-native.js';
|
|
4
4
|
const WATCH_CHAT_ID = 'linx-watch';
|
|
5
5
|
const WATCH_AGENT_ID = 'linx-watch-assistant';
|
|
6
6
|
const REMOTE_APPROVAL_POLICY_VERSION = 'linx-watch-remote-approval/v1';
|
|
7
7
|
const DEFAULT_REMOTE_APPROVAL_POLL_MS = 1000;
|
|
8
|
+
const remoteApprovalClientCache = new WeakMap();
|
|
8
9
|
function createAbortError() {
|
|
9
10
|
const error = new Error('The operation was aborted.');
|
|
10
11
|
error.name = 'AbortError';
|
|
11
12
|
return error;
|
|
12
13
|
}
|
|
13
|
-
async function dynamicImport(specifier) {
|
|
14
|
-
const loader = new Function('modulePath', 'return import(modulePath)');
|
|
15
|
-
return loader(specifier);
|
|
16
|
-
}
|
|
17
14
|
function normalizeString(value) {
|
|
18
15
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
19
16
|
}
|
|
@@ -43,10 +40,16 @@ function buildThreadUri(webId, threadId) {
|
|
|
43
40
|
return `${getPodBaseUrl(webId)}/.data/chat/${WATCH_CHAT_ID}/index.ttl#${threadId}`;
|
|
44
41
|
}
|
|
45
42
|
function buildApprovalUri(webIdOrUri, approvalId) {
|
|
46
|
-
return
|
|
43
|
+
return buildApprovalResourceUrl(webIdOrUri, approvalId);
|
|
44
|
+
}
|
|
45
|
+
function buildApprovalUriForDate(webIdOrUri, approvalId, createdAt) {
|
|
46
|
+
return buildApprovalResourceUrl(webIdOrUri, approvalId, createdAt);
|
|
47
47
|
}
|
|
48
48
|
function buildGrantUri(webIdOrUri, grantId) {
|
|
49
|
-
return
|
|
49
|
+
return buildGrantResourceUrl(webIdOrUri, grantId);
|
|
50
|
+
}
|
|
51
|
+
function buildGrantDocumentUrl(webIdOrUri) {
|
|
52
|
+
return `${getPodBaseUrl(webIdOrUri)}/settings/autonomy/grants.ttl`;
|
|
50
53
|
}
|
|
51
54
|
function buildAgentUri(webId) {
|
|
52
55
|
return `${getPodBaseUrl(webId)}/.data/agents/${WATCH_AGENT_ID}.ttl`;
|
|
@@ -203,16 +206,22 @@ function decisionFromApprovalRow(row) {
|
|
|
203
206
|
}
|
|
204
207
|
return 'accept';
|
|
205
208
|
}
|
|
206
|
-
function requestAuditForApproval(
|
|
207
|
-
const
|
|
209
|
+
function requestAuditForApproval(row, approvalUris, audits) {
|
|
210
|
+
const uriSet = new Set(approvalUris);
|
|
211
|
+
const matches = audits.filter((audit) => (audit.action === 'approval_requested'
|
|
212
|
+
&& ((audit.approval && uriSet.has(audit.approval))
|
|
213
|
+
|| (audit.session === row.session && audit.toolCallId === row.toolCallId))));
|
|
208
214
|
matches.sort((left, right) => toIsoString(right.createdAt, '').localeCompare(toIsoString(left.createdAt, '')));
|
|
209
215
|
return matches[0];
|
|
210
216
|
}
|
|
211
217
|
function normalizeApprovalSummary(row, audits) {
|
|
212
|
-
const approvalUri = buildApprovalUri(row.session, row.id);
|
|
213
|
-
const requestAudit = requestAuditForApproval(approvalUri, audits);
|
|
214
|
-
const requestContext = parseRequestAuditContext(requestAudit?.context);
|
|
215
218
|
const createdAt = toIsoString(row.createdAt, new Date(0).toISOString());
|
|
219
|
+
const approvalUris = [
|
|
220
|
+
buildApprovalUriForDate(row.session, row.id, new Date(createdAt)),
|
|
221
|
+
buildApprovalUri(row.session, row.id),
|
|
222
|
+
];
|
|
223
|
+
const requestAudit = requestAuditForApproval(row, approvalUris, audits);
|
|
224
|
+
const requestContext = parseRequestAuditContext(requestAudit?.context);
|
|
216
225
|
const sessionUri = row.session;
|
|
217
226
|
const decision = decisionFromApprovalRow(row);
|
|
218
227
|
return {
|
|
@@ -251,37 +260,11 @@ export function isRemoteApprovalAbortError(error) {
|
|
|
251
260
|
function missingRemoteApprovalCredentialsMessage() {
|
|
252
261
|
return 'LinX remote approval requires `linx login` first.';
|
|
253
262
|
}
|
|
254
|
-
function unsupportedRemoteApprovalAuthMessage() {
|
|
255
|
-
return 'LinX remote approval requires a valid LinX login in `~/.linx`.';
|
|
256
|
-
}
|
|
257
263
|
async function createDefaultRuntime() {
|
|
258
|
-
const [credentialsStore, solidAuth, oidcAuth] = await Promise.all([
|
|
259
|
-
dynamicImport(new URL('../credentials-store.js', import.meta.url).href),
|
|
260
|
-
dynamicImport(new URL('../solid-auth.js', import.meta.url).href),
|
|
261
|
-
dynamicImport(new URL('../oidc-auth.js', import.meta.url).href),
|
|
262
|
-
]);
|
|
263
264
|
return {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
authenticate: solidAuth.authenticate,
|
|
268
|
-
authenticatedFetch(url, token, init) {
|
|
269
|
-
const headers = new Headers(init?.headers);
|
|
270
|
-
headers.set('Authorization', `Bearer ${token}`);
|
|
271
|
-
return fetch(url, { ...init, headers });
|
|
272
|
-
},
|
|
273
|
-
createStore(sessionOrWebId, fetcher) {
|
|
274
|
-
const webId = typeof sessionOrWebId === 'string' ? sessionOrWebId : sessionOrWebId.info.webId;
|
|
275
|
-
if (!webId) {
|
|
276
|
-
throw new Error('Remote approval authentication succeeded without a WebID.');
|
|
277
|
-
}
|
|
278
|
-
const activeFetcher = fetcher ?? (typeof sessionOrWebId === 'string'
|
|
279
|
-
? undefined
|
|
280
|
-
: ((url, init) => sessionOrWebId.fetch(url, init)));
|
|
281
|
-
if (!activeFetcher) {
|
|
282
|
-
throw new Error('Remote approval authentication succeeded without a Pod fetcher.');
|
|
283
|
-
}
|
|
284
|
-
return createNativeRemoteApprovalStore(webId, activeFetcher);
|
|
265
|
+
getPodDataSession: getDefaultPodDataSession,
|
|
266
|
+
createStore(webId, fetcher) {
|
|
267
|
+
return createNativeRemoteApprovalStore(webId, fetcher);
|
|
285
268
|
},
|
|
286
269
|
sleep(ms) {
|
|
287
270
|
return delay(ms);
|
|
@@ -292,39 +275,43 @@ async function createDefaultRuntime() {
|
|
|
292
275
|
};
|
|
293
276
|
}
|
|
294
277
|
async function withRemoteApprovalStore(runtime, fn) {
|
|
295
|
-
const
|
|
296
|
-
if (!
|
|
278
|
+
const client = await getRemoteApprovalClient(runtime);
|
|
279
|
+
if (!client) {
|
|
297
280
|
throw new Error(missingRemoteApprovalCredentialsMessage());
|
|
298
281
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
const accessToken = await runtime.getOidcAccessToken?.(stored);
|
|
319
|
-
if (accessToken && stored.webId && runtime.authenticatedFetch) {
|
|
320
|
-
const fetcher = (url, init) => runtime.authenticatedFetch(url, accessToken, init);
|
|
321
|
-
return await fn({
|
|
322
|
-
store: runtime.createStore(stored.webId, fetcher),
|
|
323
|
-
webId: stored.webId,
|
|
324
|
-
stored,
|
|
282
|
+
return await fn({
|
|
283
|
+
store: client.store,
|
|
284
|
+
webId: client.session.webId,
|
|
285
|
+
stored: client.session.credentials,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async function getRemoteApprovalClient(runtime) {
|
|
289
|
+
let promise = remoteApprovalClientCache.get(runtime);
|
|
290
|
+
if (!promise) {
|
|
291
|
+
promise = createRemoteApprovalClient(runtime)
|
|
292
|
+
.then((client) => {
|
|
293
|
+
if (!client) {
|
|
294
|
+
remoteApprovalClientCache.delete(runtime);
|
|
295
|
+
}
|
|
296
|
+
return client;
|
|
297
|
+
})
|
|
298
|
+
.catch((error) => {
|
|
299
|
+
remoteApprovalClientCache.delete(runtime);
|
|
300
|
+
throw error;
|
|
325
301
|
});
|
|
302
|
+
remoteApprovalClientCache.set(runtime, promise);
|
|
326
303
|
}
|
|
327
|
-
|
|
304
|
+
return promise;
|
|
305
|
+
}
|
|
306
|
+
async function createRemoteApprovalClient(runtime) {
|
|
307
|
+
const session = await runtime.getPodDataSession();
|
|
308
|
+
if (!session) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
session,
|
|
313
|
+
store: runtime.createStore(session.webId, session.fetch),
|
|
314
|
+
};
|
|
328
315
|
}
|
|
329
316
|
function createNativeRemoteApprovalStore(webId, fetcher) {
|
|
330
317
|
return {
|
|
@@ -345,25 +332,30 @@ function createNativeRemoteApprovalStore(webId, fetcher) {
|
|
|
345
332
|
};
|
|
346
333
|
}
|
|
347
334
|
async function listApprovalRows(webId, fetcher) {
|
|
348
|
-
const
|
|
335
|
+
const [currentUrls, legacyUrls] = await Promise.all([
|
|
336
|
+
listTurtleResourcesRecursive(fetcher, `${getPodBaseUrl(webId)}/.data/approvals/`).catch(() => []),
|
|
337
|
+
listTurtleResources(fetcher, `${getPodBaseUrl(webId)}/.data/approvals/`).catch(() => []),
|
|
338
|
+
]);
|
|
339
|
+
const urls = [...new Set([...currentUrls, ...legacyUrls])];
|
|
349
340
|
const rows = [];
|
|
350
341
|
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
351
342
|
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
352
343
|
if (!turtle)
|
|
353
344
|
continue;
|
|
354
|
-
const predicates
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
rows.push(row);
|
|
345
|
+
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
346
|
+
const row = approvalRowFromPredicates(subject, predicates);
|
|
347
|
+
if (row)
|
|
348
|
+
rows.push(row);
|
|
349
|
+
}
|
|
360
350
|
}
|
|
361
351
|
return rows;
|
|
362
352
|
}
|
|
363
353
|
async function writeApprovalRow(webId, fetcher, row) {
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
354
|
+
const createdAt = new Date(toIsoString(row.createdAt, new Date().toISOString()));
|
|
355
|
+
const documentUrl = buildApprovalDocumentUrl(webId, createdAt);
|
|
356
|
+
const subjectUrl = buildApprovalResourceUrl(webId, row.id, createdAt);
|
|
357
|
+
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
358
|
+
subject: subjectUrl,
|
|
367
359
|
triples: [
|
|
368
360
|
{ predicate: RDF_TYPE, object: iri(UDFS_APPROVAL_REQUEST) },
|
|
369
361
|
{ predicate: UDFS_SESSION, object: iri(row.session) },
|
|
@@ -385,25 +377,26 @@ async function writeApprovalRow(webId, fetcher, row) {
|
|
|
385
377
|
});
|
|
386
378
|
}
|
|
387
379
|
async function listAuditRows(webId, fetcher) {
|
|
388
|
-
const urls = await
|
|
380
|
+
const urls = await listTurtleResourcesRecursive(fetcher, `${getPodBaseUrl(webId)}/.data/audits/`);
|
|
389
381
|
const rows = [];
|
|
390
382
|
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
391
383
|
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
392
384
|
if (!turtle)
|
|
393
385
|
continue;
|
|
394
|
-
const predicates
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
rows.push(row);
|
|
386
|
+
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
387
|
+
const row = auditRowFromPredicates(subject, predicates);
|
|
388
|
+
if (row)
|
|
389
|
+
rows.push(row);
|
|
390
|
+
}
|
|
400
391
|
}
|
|
401
392
|
return rows;
|
|
402
393
|
}
|
|
403
394
|
async function writeAuditRow(webId, fetcher, row) {
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
395
|
+
const createdAt = new Date(toIsoString(row.createdAt, new Date().toISOString()));
|
|
396
|
+
const documentUrl = buildAuditDocumentUrl(webId, createdAt);
|
|
397
|
+
const subjectUrl = buildAuditResourceUrl(webId, row.id, createdAt);
|
|
398
|
+
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
399
|
+
subject: subjectUrl,
|
|
407
400
|
triples: [
|
|
408
401
|
{ predicate: RDF_TYPE, object: iri(UDFS_AUDIT_ENTRY) },
|
|
409
402
|
{ predicate: UDFS_ACTION, object: literal(row.action) },
|
|
@@ -420,24 +413,27 @@ async function writeAuditRow(webId, fetcher, row) {
|
|
|
420
413
|
});
|
|
421
414
|
}
|
|
422
415
|
async function listGrantRows(webId, fetcher) {
|
|
423
|
-
const urls =
|
|
416
|
+
const urls = [
|
|
417
|
+
`${getPodBaseUrl(webId)}/settings/autonomy/grants.ttl`,
|
|
418
|
+
...await listTurtleResources(fetcher, `${getPodBaseUrl(webId)}/settings/autonomy/grants/`).catch(() => []),
|
|
419
|
+
];
|
|
424
420
|
const rows = [];
|
|
425
421
|
for (const url of urls.filter((entry) => entry.endsWith('.ttl'))) {
|
|
426
422
|
const turtle = await readTurtleResource(fetcher, url).catch(() => null);
|
|
427
423
|
if (!turtle)
|
|
428
424
|
continue;
|
|
429
|
-
const predicates
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
rows.push(row);
|
|
425
|
+
for (const [subject, predicates] of parseManagedTurtleBlocks(turtle, url)) {
|
|
426
|
+
const row = grantRowFromPredicates(subject, predicates);
|
|
427
|
+
if (row)
|
|
428
|
+
rows.push(row);
|
|
429
|
+
}
|
|
435
430
|
}
|
|
436
431
|
return rows;
|
|
437
432
|
}
|
|
438
433
|
async function writeGrantRow(webId, fetcher, row) {
|
|
439
434
|
const id = normalizeString(row.id) ?? crypto.randomUUID();
|
|
440
|
-
const
|
|
435
|
+
const documentUrl = buildGrantDocumentUrl(webId);
|
|
436
|
+
const subjectUrl = buildGrantResourceUrl(webId, id);
|
|
441
437
|
const target = normalizeString(row.target);
|
|
442
438
|
const action = normalizeString(row.action);
|
|
443
439
|
const effect = normalizeString(row.effect);
|
|
@@ -446,8 +442,8 @@ async function writeGrantRow(webId, fetcher, row) {
|
|
|
446
442
|
if (!target || !action || !effect || !decisionBy || !decisionRole) {
|
|
447
443
|
throw new Error(`Invalid remote approval grant row: ${id}`);
|
|
448
444
|
}
|
|
449
|
-
await upsertManagedTurtleBlock(fetcher,
|
|
450
|
-
subject:
|
|
445
|
+
await upsertManagedTurtleBlock(fetcher, documentUrl, {
|
|
446
|
+
subject: subjectUrl,
|
|
451
447
|
triples: [
|
|
452
448
|
{ predicate: RDF_TYPE, object: iri(ODRL_POLICY) },
|
|
453
449
|
{ predicate: RDF_TYPE, object: iri(UDFS_AUTONOMY_GRANT) },
|
|
@@ -585,7 +581,7 @@ export async function createRemoteApproval(options) {
|
|
|
585
581
|
const approvalId = crypto.randomUUID();
|
|
586
582
|
const now = activeRuntime.now();
|
|
587
583
|
const sessionUri = subject.sessionUri;
|
|
588
|
-
const approvalUri =
|
|
584
|
+
const approvalUri = buildApprovalUriForDate(webId, approvalId, now);
|
|
589
585
|
const targetUri = subject.targetUri ?? sessionUri;
|
|
590
586
|
const assignedTo = subject.assignedTo ?? webId;
|
|
591
587
|
const onBehalfOf = subject.onBehalfOf ?? webId;
|
|
@@ -765,7 +761,8 @@ export async function resolveRemoteWatchApproval(options) {
|
|
|
765
761
|
return normalizeApprovalSummary(row, audits);
|
|
766
762
|
}
|
|
767
763
|
const now = activeRuntime.now();
|
|
768
|
-
const
|
|
764
|
+
const approvalCreatedAt = new Date(toIsoString(row.createdAt, now.toISOString()));
|
|
765
|
+
const approvalUri = buildApprovalUriForDate(row.session, row.id, approvalCreatedAt);
|
|
769
766
|
const nextStatus = options.decision === 'accept' || options.decision === 'accept_for_session'
|
|
770
767
|
? 'approved'
|
|
771
768
|
: 'rejected';
|