holosphere 1.1.20 → 2.0.0-alpha0
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/.env.example +36 -0
- package/.eslintrc.json +16 -0
- package/.prettierrc.json +7 -0
- package/README.md +483 -367
- package/bin/holosphere-activitypub.js +158 -0
- package/cleanup-test-data.js +204 -0
- package/examples/demo.html +1333 -0
- package/examples/example-bot.js +197 -0
- package/package.json +47 -87
- package/scripts/check-bundle-size.js +54 -0
- package/scripts/check-quest-ids.js +77 -0
- package/scripts/import-holons.js +578 -0
- package/scripts/publish-to-relay.js +101 -0
- package/scripts/read-example.js +186 -0
- package/scripts/relay-diagnostic.js +59 -0
- package/scripts/relay-example.js +179 -0
- package/scripts/resync-to-relay.js +245 -0
- package/scripts/revert-import.js +196 -0
- package/scripts/test-hybrid-mode.js +108 -0
- package/scripts/test-local-storage.js +63 -0
- package/scripts/test-nostr-direct.js +55 -0
- package/scripts/test-read-data.js +45 -0
- package/scripts/test-write-read.js +63 -0
- package/scripts/verify-import.js +95 -0
- package/scripts/verify-relay-data.js +139 -0
- package/src/ai/aggregation.js +319 -0
- package/src/ai/breakdown.js +511 -0
- package/src/ai/classifier.js +217 -0
- package/src/ai/council.js +228 -0
- package/src/ai/embeddings.js +279 -0
- package/src/ai/federation-ai.js +324 -0
- package/src/ai/h3-ai.js +955 -0
- package/src/ai/index.js +112 -0
- package/src/ai/json-ops.js +225 -0
- package/src/ai/llm-service.js +205 -0
- package/src/ai/nl-query.js +223 -0
- package/src/ai/relationships.js +353 -0
- package/src/ai/schema-extractor.js +218 -0
- package/src/ai/spatial.js +293 -0
- package/src/ai/tts.js +194 -0
- package/src/content/social-protocols.js +168 -0
- package/src/core/holosphere.js +273 -0
- package/src/crypto/secp256k1.js +259 -0
- package/src/federation/discovery.js +334 -0
- package/src/federation/hologram.js +1042 -0
- package/src/federation/registry.js +386 -0
- package/src/hierarchical/upcast.js +110 -0
- package/src/index.js +2669 -0
- package/src/schema/validator.js +91 -0
- package/src/spatial/h3-operations.js +110 -0
- package/src/storage/backend-factory.js +125 -0
- package/src/storage/backend-interface.js +142 -0
- package/src/storage/backends/activitypub/server.js +653 -0
- package/src/storage/backends/activitypub-backend.js +272 -0
- package/src/storage/backends/gundb-backend.js +233 -0
- package/src/storage/backends/nostr-backend.js +136 -0
- package/src/storage/filesystem-storage-browser.js +41 -0
- package/src/storage/filesystem-storage.js +138 -0
- package/src/storage/global-tables.js +81 -0
- package/src/storage/gun-async.js +281 -0
- package/src/storage/gun-wrapper.js +221 -0
- package/src/storage/indexeddb-storage.js +122 -0
- package/src/storage/key-storage-simple.js +76 -0
- package/src/storage/key-storage.js +136 -0
- package/src/storage/memory-storage.js +59 -0
- package/src/storage/migration.js +338 -0
- package/src/storage/nostr-async.js +811 -0
- package/src/storage/nostr-client.js +939 -0
- package/src/storage/nostr-wrapper.js +211 -0
- package/src/storage/outbox-queue.js +208 -0
- package/src/storage/persistent-storage.js +109 -0
- package/src/storage/sync-service.js +164 -0
- package/src/subscriptions/manager.js +142 -0
- package/test-ai-real-api.js +202 -0
- package/tests/unit/ai/aggregation.test.js +295 -0
- package/tests/unit/ai/breakdown.test.js +446 -0
- package/tests/unit/ai/classifier.test.js +294 -0
- package/tests/unit/ai/council.test.js +262 -0
- package/tests/unit/ai/embeddings.test.js +384 -0
- package/tests/unit/ai/federation-ai.test.js +344 -0
- package/tests/unit/ai/h3-ai.test.js +458 -0
- package/tests/unit/ai/index.test.js +304 -0
- package/tests/unit/ai/json-ops.test.js +307 -0
- package/tests/unit/ai/llm-service.test.js +390 -0
- package/tests/unit/ai/nl-query.test.js +383 -0
- package/tests/unit/ai/relationships.test.js +311 -0
- package/tests/unit/ai/schema-extractor.test.js +384 -0
- package/tests/unit/ai/spatial.test.js +279 -0
- package/tests/unit/ai/tts.test.js +279 -0
- package/tests/unit/content.test.js +332 -0
- package/tests/unit/contract/core.test.js +88 -0
- package/tests/unit/contract/crypto.test.js +198 -0
- package/tests/unit/contract/data.test.js +223 -0
- package/tests/unit/contract/federation.test.js +181 -0
- package/tests/unit/contract/hierarchical.test.js +113 -0
- package/tests/unit/contract/schema.test.js +114 -0
- package/tests/unit/contract/social.test.js +217 -0
- package/tests/unit/contract/spatial.test.js +110 -0
- package/tests/unit/contract/subscriptions.test.js +128 -0
- package/tests/unit/contract/utils.test.js +159 -0
- package/tests/unit/core.test.js +152 -0
- package/tests/unit/crypto.test.js +328 -0
- package/tests/unit/federation.test.js +234 -0
- package/tests/unit/gun-async.test.js +252 -0
- package/tests/unit/hierarchical.test.js +399 -0
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
- package/tests/unit/integration/scenario-02-federation.test.js +76 -0
- package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
- package/tests/unit/integration/scenario-04-validation.test.js +129 -0
- package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
- package/tests/unit/integration/scenario-06-social.test.js +135 -0
- package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
- package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
- package/tests/unit/performance/benchmark.test.js +85 -0
- package/tests/unit/schema.test.js +213 -0
- package/tests/unit/spatial.test.js +158 -0
- package/tests/unit/storage.test.js +195 -0
- package/tests/unit/subscriptions.test.js +328 -0
- package/tests/unit/test-data-permanence-debug.js +197 -0
- package/tests/unit/test-data-permanence.js +340 -0
- package/tests/unit/test-key-persistence-fixed.js +148 -0
- package/tests/unit/test-key-persistence.js +172 -0
- package/tests/unit/test-relay-permanence.js +376 -0
- package/tests/unit/test-second-node.js +95 -0
- package/tests/unit/test-simple-write.js +89 -0
- package/vite.config.js +49 -0
- package/vitest.config.js +20 -0
- package/FEDERATION.md +0 -213
- package/compute.js +0 -298
- package/content.js +0 -980
- package/federation.js +0 -1234
- package/global.js +0 -736
- package/hexlib.js +0 -335
- package/hologram.js +0 -183
- package/holosphere-bundle.esm.js +0 -33256
- package/holosphere-bundle.js +0 -33287
- package/holosphere-bundle.min.js +0 -39
- package/holosphere.d.ts +0 -601
- package/holosphere.js +0 -719
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nostr-based Federation Discovery Protocol
|
|
3
|
+
* Optional feature for automated federation handshake via Nostr events
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { issueCapability } from '../crypto/secp256k1.js';
|
|
7
|
+
import { addFederatedPartner, storeInboundCapability } from './registry.js';
|
|
8
|
+
|
|
9
|
+
// Custom event kinds for federation protocol
|
|
10
|
+
const FEDERATION_REQUEST_KIND = 30078;
|
|
11
|
+
const FEDERATION_ACCEPT_KIND = 30079;
|
|
12
|
+
const FEDERATION_DECLINE_KIND = 30080;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Send a federation request to another holosphere via Nostr
|
|
16
|
+
* @param {Object} client - NostrClient instance
|
|
17
|
+
* @param {string} appname - Application namespace
|
|
18
|
+
* @param {string} targetPubKey - Target holosphere's public key
|
|
19
|
+
* @param {Object} options - Request options
|
|
20
|
+
* @param {Object} options.scope - Requested scope { holonId, lensName }
|
|
21
|
+
* @param {string[]} options.permissions - Requested permissions
|
|
22
|
+
* @param {string} options.message - Optional message to include
|
|
23
|
+
* @param {number} options.expiresIn - Token expiration in ms (default: 30 days)
|
|
24
|
+
* @returns {Promise<Object>} Published event info
|
|
25
|
+
*/
|
|
26
|
+
export async function sendFederationRequest(client, appname, targetPubKey, options = {}) {
|
|
27
|
+
const {
|
|
28
|
+
scope = { holonId: '*', lensName: '*' },
|
|
29
|
+
permissions = ['read'],
|
|
30
|
+
message = 'Federation request',
|
|
31
|
+
expiresIn = 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
32
|
+
} = options;
|
|
33
|
+
|
|
34
|
+
// Issue capability we're offering them (access to our data)
|
|
35
|
+
const offeredCapability = await issueCapability(
|
|
36
|
+
permissions,
|
|
37
|
+
scope,
|
|
38
|
+
targetPubKey,
|
|
39
|
+
{ issuerKey: client.privateKey, expiresIn }
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const event = {
|
|
43
|
+
kind: FEDERATION_REQUEST_KIND,
|
|
44
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
45
|
+
tags: [
|
|
46
|
+
['d', `federation-request-${targetPubKey.slice(0, 16)}`],
|
|
47
|
+
['p', targetPubKey],
|
|
48
|
+
['holosphere', appname],
|
|
49
|
+
['scope', JSON.stringify(scope)],
|
|
50
|
+
['permissions', permissions.join(',')],
|
|
51
|
+
],
|
|
52
|
+
content: JSON.stringify({
|
|
53
|
+
message,
|
|
54
|
+
offeredCapability,
|
|
55
|
+
requestedScope: scope,
|
|
56
|
+
requestedPermissions: permissions,
|
|
57
|
+
}),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const result = await client.publish(event);
|
|
61
|
+
return {
|
|
62
|
+
eventId: result.id || null,
|
|
63
|
+
targetPubKey,
|
|
64
|
+
scope,
|
|
65
|
+
permissions,
|
|
66
|
+
offeredCapability,
|
|
67
|
+
...result,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Subscribe to incoming federation requests
|
|
73
|
+
* @param {Object} client - NostrClient instance
|
|
74
|
+
* @param {Function} callback - Called with each request
|
|
75
|
+
* @returns {Promise<Object>} Subscription with unsubscribe method
|
|
76
|
+
*/
|
|
77
|
+
export async function subscribeFederationRequests(client, callback) {
|
|
78
|
+
const filter = {
|
|
79
|
+
kinds: [FEDERATION_REQUEST_KIND],
|
|
80
|
+
'#p': [client.publicKey],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return client.subscribe(filter, (event) => {
|
|
84
|
+
try {
|
|
85
|
+
const content = JSON.parse(event.content);
|
|
86
|
+
const scopeTag = event.tags.find(t => t[0] === 'scope');
|
|
87
|
+
const permissionsTag = event.tags.find(t => t[0] === 'permissions');
|
|
88
|
+
const holosphereTag = event.tags.find(t => t[0] === 'holosphere');
|
|
89
|
+
|
|
90
|
+
const request = {
|
|
91
|
+
eventId: event.id,
|
|
92
|
+
requesterPubKey: event.pubkey,
|
|
93
|
+
appname: holosphereTag?.[1],
|
|
94
|
+
scope: scopeTag ? JSON.parse(scopeTag[1]) : { holonId: '*', lensName: '*' },
|
|
95
|
+
permissions: permissionsTag ? permissionsTag[1].split(',') : ['read'],
|
|
96
|
+
message: content.message,
|
|
97
|
+
offeredCapability: content.offeredCapability,
|
|
98
|
+
timestamp: event.created_at,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
callback(request);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Failed to parse federation request:', error);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Accept a federation request
|
|
110
|
+
* @param {Object} client - NostrClient instance
|
|
111
|
+
* @param {string} appname - Application namespace
|
|
112
|
+
* @param {Object} request - Request object from subscription
|
|
113
|
+
* @param {Object} options - Acceptance options
|
|
114
|
+
* @param {Object} options.scope - Scope we're granting (defaults to requested)
|
|
115
|
+
* @param {string[]} options.permissions - Permissions we're granting
|
|
116
|
+
* @param {string} options.alias - Alias for this partner
|
|
117
|
+
* @param {number} options.expiresIn - Token expiration in ms
|
|
118
|
+
* @returns {Promise<Object>} Published acceptance event info
|
|
119
|
+
*/
|
|
120
|
+
export async function acceptFederationRequest(client, appname, request, options = {}) {
|
|
121
|
+
const {
|
|
122
|
+
scope = request.scope,
|
|
123
|
+
permissions = request.permissions,
|
|
124
|
+
alias = null,
|
|
125
|
+
expiresIn = 30 * 24 * 60 * 60 * 1000,
|
|
126
|
+
} = options;
|
|
127
|
+
|
|
128
|
+
// Issue our capability to them
|
|
129
|
+
const grantedCapability = await issueCapability(
|
|
130
|
+
permissions,
|
|
131
|
+
scope,
|
|
132
|
+
request.requesterPubKey,
|
|
133
|
+
{ issuerKey: client.privateKey, expiresIn }
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Store their offered capability in our registry
|
|
137
|
+
await addFederatedPartner(client, appname, request.requesterPubKey, {
|
|
138
|
+
alias,
|
|
139
|
+
addedVia: 'nostr_discovery',
|
|
140
|
+
inboundCapabilities: [{
|
|
141
|
+
token: request.offeredCapability,
|
|
142
|
+
scope: request.scope,
|
|
143
|
+
permissions: request.permissions,
|
|
144
|
+
expires: Date.now() + expiresIn,
|
|
145
|
+
}],
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Publish acceptance event
|
|
149
|
+
const event = {
|
|
150
|
+
kind: FEDERATION_ACCEPT_KIND,
|
|
151
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
152
|
+
tags: [
|
|
153
|
+
['d', `federation-accept-${request.requesterPubKey.slice(0, 16)}`],
|
|
154
|
+
['p', request.requesterPubKey],
|
|
155
|
+
['e', request.eventId], // Reference to original request
|
|
156
|
+
['holosphere', appname],
|
|
157
|
+
['scope', JSON.stringify(scope)],
|
|
158
|
+
['permissions', permissions.join(',')],
|
|
159
|
+
],
|
|
160
|
+
content: JSON.stringify({
|
|
161
|
+
acceptedScope: scope,
|
|
162
|
+
grantedCapability,
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const result = await client.publish(event);
|
|
167
|
+
return {
|
|
168
|
+
eventId: result.id || null,
|
|
169
|
+
requesterPubKey: request.requesterPubKey,
|
|
170
|
+
grantedCapability,
|
|
171
|
+
acceptedScope: scope,
|
|
172
|
+
...result,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Decline a federation request
|
|
178
|
+
* @param {Object} client - NostrClient instance
|
|
179
|
+
* @param {string} appname - Application namespace
|
|
180
|
+
* @param {Object} request - Request object from subscription
|
|
181
|
+
* @param {string} reason - Optional decline reason
|
|
182
|
+
* @returns {Promise<Object>} Published decline event info
|
|
183
|
+
*/
|
|
184
|
+
export async function declineFederationRequest(client, appname, request, reason = '') {
|
|
185
|
+
const event = {
|
|
186
|
+
kind: FEDERATION_DECLINE_KIND,
|
|
187
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
188
|
+
tags: [
|
|
189
|
+
['d', `federation-decline-${request.requesterPubKey.slice(0, 16)}`],
|
|
190
|
+
['p', request.requesterPubKey],
|
|
191
|
+
['e', request.eventId],
|
|
192
|
+
['holosphere', appname],
|
|
193
|
+
],
|
|
194
|
+
content: JSON.stringify({ reason }),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const result = await client.publish(event);
|
|
198
|
+
return {
|
|
199
|
+
eventId: result.id || null,
|
|
200
|
+
requesterPubKey: request.requesterPubKey,
|
|
201
|
+
reason,
|
|
202
|
+
...result,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Subscribe to federation acceptances (responses to our requests)
|
|
208
|
+
* @param {Object} client - NostrClient instance
|
|
209
|
+
* @param {string} appname - Application namespace
|
|
210
|
+
* @param {Function} callback - Called with each acceptance
|
|
211
|
+
* @returns {Promise<Object>} Subscription with unsubscribe method
|
|
212
|
+
*/
|
|
213
|
+
export async function subscribeFederationAcceptances(client, appname, callback) {
|
|
214
|
+
const filter = {
|
|
215
|
+
kinds: [FEDERATION_ACCEPT_KIND],
|
|
216
|
+
'#p': [client.publicKey],
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
return client.subscribe(filter, async (event) => {
|
|
220
|
+
try {
|
|
221
|
+
const content = JSON.parse(event.content);
|
|
222
|
+
const scopeTag = event.tags.find(t => t[0] === 'scope');
|
|
223
|
+
const permissionsTag = event.tags.find(t => t[0] === 'permissions');
|
|
224
|
+
|
|
225
|
+
// Store their granted capability in our registry
|
|
226
|
+
await storeInboundCapability(client, appname, event.pubkey, {
|
|
227
|
+
token: content.grantedCapability,
|
|
228
|
+
scope: scopeTag ? JSON.parse(scopeTag[1]) : content.acceptedScope,
|
|
229
|
+
permissions: permissionsTag ? permissionsTag[1].split(',') : ['read'],
|
|
230
|
+
expires: Date.now() + 30 * 24 * 60 * 60 * 1000, // Assume 30 day expiry
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
callback({
|
|
234
|
+
accepterPubKey: event.pubkey,
|
|
235
|
+
originalRequestId: event.tags.find(t => t[0] === 'e')?.[1],
|
|
236
|
+
acceptedScope: content.acceptedScope,
|
|
237
|
+
grantedCapability: content.grantedCapability,
|
|
238
|
+
timestamp: event.created_at,
|
|
239
|
+
});
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error('Failed to parse federation acceptance:', error);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Subscribe to federation declines
|
|
248
|
+
* @param {Object} client - NostrClient instance
|
|
249
|
+
* @param {Function} callback - Called with each decline
|
|
250
|
+
* @returns {Promise<Object>} Subscription with unsubscribe method
|
|
251
|
+
*/
|
|
252
|
+
export async function subscribeFederationDeclines(client, callback) {
|
|
253
|
+
const filter = {
|
|
254
|
+
kinds: [FEDERATION_DECLINE_KIND],
|
|
255
|
+
'#p': [client.publicKey],
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
return client.subscribe(filter, (event) => {
|
|
259
|
+
try {
|
|
260
|
+
const content = JSON.parse(event.content);
|
|
261
|
+
|
|
262
|
+
callback({
|
|
263
|
+
declinerPubKey: event.pubkey,
|
|
264
|
+
originalRequestId: event.tags.find(t => t[0] === 'e')?.[1],
|
|
265
|
+
reason: content.reason || '',
|
|
266
|
+
timestamp: event.created_at,
|
|
267
|
+
});
|
|
268
|
+
} catch (error) {
|
|
269
|
+
console.error('Failed to parse federation decline:', error);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Query for pending federation requests (requests we've sent that haven't been answered)
|
|
276
|
+
* @param {Object} client - NostrClient instance
|
|
277
|
+
* @param {Object} options - Query options
|
|
278
|
+
* @param {number} options.since - Only get requests since this timestamp (seconds)
|
|
279
|
+
* @returns {Promise<Object[]>} Array of pending requests
|
|
280
|
+
*/
|
|
281
|
+
export async function getPendingFederationRequests(client, options = {}) {
|
|
282
|
+
const since = options.since || Math.floor((Date.now() - 30 * 24 * 60 * 60 * 1000) / 1000);
|
|
283
|
+
|
|
284
|
+
// Get our requests
|
|
285
|
+
const requestFilter = {
|
|
286
|
+
kinds: [FEDERATION_REQUEST_KIND],
|
|
287
|
+
authors: [client.publicKey],
|
|
288
|
+
since,
|
|
289
|
+
};
|
|
290
|
+
const requests = await client.query(requestFilter);
|
|
291
|
+
|
|
292
|
+
// Get all acceptances and declines to our requests
|
|
293
|
+
const responseFilter = {
|
|
294
|
+
kinds: [FEDERATION_ACCEPT_KIND, FEDERATION_DECLINE_KIND],
|
|
295
|
+
'#p': [client.publicKey],
|
|
296
|
+
since,
|
|
297
|
+
};
|
|
298
|
+
const responses = await client.query(responseFilter);
|
|
299
|
+
|
|
300
|
+
// Find request event IDs that have been responded to
|
|
301
|
+
const respondedRequestIds = new Set();
|
|
302
|
+
for (const response of responses) {
|
|
303
|
+
const eTag = response.tags.find(t => t[0] === 'e');
|
|
304
|
+
if (eTag) {
|
|
305
|
+
respondedRequestIds.add(eTag[1]);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Filter to only pending requests
|
|
310
|
+
const pending = [];
|
|
311
|
+
for (const request of requests) {
|
|
312
|
+
if (!respondedRequestIds.has(request.id)) {
|
|
313
|
+
try {
|
|
314
|
+
const content = JSON.parse(request.content);
|
|
315
|
+
const pTag = request.tags.find(t => t[0] === 'p');
|
|
316
|
+
const scopeTag = request.tags.find(t => t[0] === 'scope');
|
|
317
|
+
const permissionsTag = request.tags.find(t => t[0] === 'permissions');
|
|
318
|
+
|
|
319
|
+
pending.push({
|
|
320
|
+
eventId: request.id,
|
|
321
|
+
targetPubKey: pTag?.[1],
|
|
322
|
+
scope: scopeTag ? JSON.parse(scopeTag[1]) : { holonId: '*', lensName: '*' },
|
|
323
|
+
permissions: permissionsTag ? permissionsTag[1].split(',') : ['read'],
|
|
324
|
+
message: content.message,
|
|
325
|
+
timestamp: request.created_at,
|
|
326
|
+
});
|
|
327
|
+
} catch (error) {
|
|
328
|
+
console.error('Failed to parse pending request:', error);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return pending;
|
|
334
|
+
}
|