@agentshouse/mdruntime-adapter 0.1.0-cdc1a38081f09 → 0.1.0-cde42e60258b9
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 +2 -2
- package/dist/actions.d.ts +6 -59
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +22 -292
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +158 -358
- package/dist/app-reference.d.ts +2 -0
- package/dist/app-reference.d.ts.map +1 -0
- package/dist/app-reference.js +2 -0
- package/dist/chunks.d.ts +38 -0
- package/dist/chunks.d.ts.map +1 -0
- package/dist/chunks.js +247 -0
- package/dist/crockford.d.ts +4 -0
- package/dist/crockford.d.ts.map +1 -0
- package/dist/crockford.js +3 -0
- package/dist/discovery.d.ts +22 -15
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +75 -56
- package/dist/history.d.ts +12 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +92 -0
- package/dist/house-model.d.ts +682 -0
- package/dist/house-model.d.ts.map +1 -0
- package/dist/house-model.js +396 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/model.d.ts +5 -8
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +32 -46
- package/dist/read.d.ts +21 -59
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +50 -330
- package/dist/refusal.d.ts +1 -3
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +4 -5
- package/dist/room-types.d.ts +2 -3
- package/dist/room-types.d.ts.map +1 -1
- package/dist/room-types.js +49 -12
- package/dist/search.d.ts +5 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +47 -0
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +75 -167
- package/dist/space-schema.d.ts +7 -0
- package/dist/space-schema.d.ts.map +1 -0
- package/dist/space-schema.js +18 -0
- package/dist/stream.d.ts +16 -15
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +233 -423
- package/dist/transport.d.ts +47 -36
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +54 -11
- package/dist/upload.d.ts.map +1 -1
- package/dist/upload.js +2 -2
- package/dist/write.d.ts +6 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +21 -30
- package/package.json +7 -3
- package/AGENTS.md +0 -317
- package/dist/gather.d.ts +0 -4
- package/dist/gather.d.ts.map +0 -1
- package/dist/gather.js +0 -35
- package/dist/part.d.ts +0 -15
- package/dist/part.d.ts.map +0 -1
- package/dist/part.js +0 -82
package/dist/adapter.js
CHANGED
|
@@ -1,210 +1,123 @@
|
|
|
1
1
|
import { stepped } from '@agentshouse/mdruntime';
|
|
2
|
-
import {
|
|
3
|
-
import { manifestedModels, partModels, streamedModels, viewModels } from './model.js';
|
|
4
|
-
import { gathered, gatheredEach, selections } from './gather.js';
|
|
2
|
+
import { capabilities, discoveredRoom, mapDiscovery, parseAuthorityChange, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
5
3
|
import { createSiteAdapter } from './site.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import { parseFileAddress, parseGrant } from './read.js';
|
|
5
|
+
import { chunkedHead, chunkLister, chunkPart } from './chunks.js';
|
|
6
|
+
import { spaceSchemas } from './space-schema.js';
|
|
9
7
|
import { finishedUpload, houseUpload } from './upload.js';
|
|
10
|
-
import { houseAction } from './actions.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
8
|
+
import { houseAction, HOUSE_LEADING_READS, HOUSE_UNKEPT_READS } from './actions.js';
|
|
9
|
+
import { houseSearch } from './search.js';
|
|
10
|
+
import { classifyDoor, doorRefusal } from './refusal.js';
|
|
11
|
+
import { houseNotifications, } from './stream.js';
|
|
12
|
+
import { houseDoorRoute, } from './transport.js';
|
|
14
13
|
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import { HOUSE_TYPED_PATHS } from './house-model.js';
|
|
15
|
+
import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
|
|
16
|
+
import { followsVersion, reachedVersion, roomVersion } from './version.js';
|
|
17
17
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
18
18
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
19
|
+
const OCTET_STREAM = 'application/octet-stream';
|
|
19
20
|
const EXPIRED_ADDRESS = 403;
|
|
20
21
|
function lapsedGrant(outcome) {
|
|
21
22
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
22
23
|
}
|
|
23
|
-
async function heldFiles(authority, held, bound) {
|
|
24
|
-
const listed = await held.files(null, bound + 1);
|
|
25
|
-
const files = listed.slice(0, bound);
|
|
26
|
-
const through = listed.length > bound ? (files.at(-1)?.path ?? null) : null;
|
|
27
|
-
return files.length === 0 ? null : { room: authority, through, files };
|
|
28
|
-
}
|
|
29
|
-
async function reused(scope, held) {
|
|
30
|
-
const sent = new Set(scope.contents.map((entry) => entry.path));
|
|
31
|
-
const contents = [...scope.contents];
|
|
32
|
-
const asked = scope.files.filter((file) => file.binary === undefined && !sent.has(file.path));
|
|
33
|
-
if (asked.length === 0)
|
|
34
|
-
return scope;
|
|
35
|
-
if (held === undefined)
|
|
36
|
-
return null;
|
|
37
|
-
const kept = await held.contents(asked.map(({ path }) => path));
|
|
38
|
-
for (const file of asked) {
|
|
39
|
-
const found = kept.get(file.path);
|
|
40
|
-
if (found === undefined ||
|
|
41
|
-
found.revision !== file.revision ||
|
|
42
|
-
(await sha256Hex(found.content)) !== file.sha256) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
contents.push({ path: file.path, content: found.content });
|
|
46
|
-
}
|
|
47
|
-
return { ...scope, contents };
|
|
48
|
-
}
|
|
49
24
|
export function createAgentsHouseAdapter(options) {
|
|
50
25
|
if ('site' in options)
|
|
51
26
|
return createSiteAdapter(options.site, options.snapshot === true);
|
|
52
27
|
const transport = options.transport;
|
|
53
|
-
const
|
|
28
|
+
const spaces = spaceSchemas();
|
|
29
|
+
const notifications = houseNotifications(transport, 'credential', {
|
|
30
|
+
opened: async (frame) => (frame === null ? null : opened(frame)),
|
|
31
|
+
changed,
|
|
32
|
+
expired: (authorities) => lister.askAll(authorities.flatMap((authority) => [...tracked.values()].filter((entry) => entry.authority === authority))),
|
|
33
|
+
}, spaces);
|
|
54
34
|
const tracked = new Map();
|
|
55
|
-
const
|
|
56
|
-
|
|
35
|
+
const lister = chunkLister(transport, () => declared().max_prefixes);
|
|
36
|
+
const chunked = new Map();
|
|
37
|
+
const renewals = new WeakMap();
|
|
57
38
|
let bounds;
|
|
58
39
|
function declared() {
|
|
59
40
|
if (bounds === undefined)
|
|
60
41
|
throw new Error('no Discovery declared the bounds');
|
|
61
42
|
return bounds;
|
|
62
43
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
for (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
part: listed.part,
|
|
74
|
-
...(continued.held === undefined ? {} : { held: [continued.held] }),
|
|
75
|
-
}, partArrival(listed, arriving));
|
|
76
|
-
const failure = classifyRead(answered.status, answered.body, entry.locator);
|
|
77
|
-
if (failure !== null)
|
|
78
|
-
return failure;
|
|
79
|
-
const frame = partFrame(answered.body);
|
|
80
|
-
if (frame === null)
|
|
81
|
-
continue;
|
|
82
|
-
const scope = await housePart(continued.room, frame, listed, 'credential');
|
|
83
|
-
if (scope !== null)
|
|
84
|
-
return 'kind' in scope ? scope : { scope, frame };
|
|
85
|
-
}
|
|
86
|
-
return unverifiedPart(entry.locator);
|
|
87
|
-
}
|
|
88
|
-
async function manifestPage(asked, continued) {
|
|
89
|
-
const inventory = partInventory(continued.room);
|
|
90
|
-
if (inventory.parts === 0) {
|
|
91
|
-
const page = await partPage(asked, continued, undefined, undefined);
|
|
92
|
-
return page.kind === 'bootstrap' ? { ...page, inventory } : page;
|
|
93
|
-
}
|
|
94
|
-
const { models, seen } = await manifestedModels(continued.room, asked.entry);
|
|
95
|
-
return manifested(continued.room, inventory, JSON.stringify({ ...continued, seen }), models);
|
|
96
|
-
}
|
|
97
|
-
async function partPage(asked, continued, received, arriving) {
|
|
98
|
-
const { entry } = asked;
|
|
99
|
-
const listed = continued.room.parts[continued.next];
|
|
100
|
-
const sent = listed === undefined
|
|
101
|
-
? { scope: emptyScope(continued.room), frame: null }
|
|
102
|
-
: await fetchedPart(entry, continued, listed, received, arriving);
|
|
103
|
-
if ('kind' in sent)
|
|
104
|
-
return sent;
|
|
105
|
-
const scope = await reused(sent.scope, asked.held);
|
|
106
|
-
if (scope === null) {
|
|
107
|
-
return asked.held === undefined
|
|
108
|
-
? UNCERTAIN_READ
|
|
109
|
-
: bootstrapTogether({ entry, held: undefined });
|
|
110
|
-
}
|
|
111
|
-
const next = continued.next + 1;
|
|
112
|
-
const more = next < continued.room.parts.length;
|
|
113
|
-
if (continued.next === 0 && !more)
|
|
114
|
-
handed.set(entry.locator, roomVersion(scope));
|
|
115
|
-
else
|
|
116
|
-
handed.delete(entry.locator);
|
|
117
|
-
notifications.reach(entry.authority, scope);
|
|
118
|
-
const seen = [
|
|
119
|
-
...continued.seen,
|
|
120
|
-
...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
|
|
121
|
-
];
|
|
122
|
-
const models = await partModels(scope, entry, new Set(seen), !more);
|
|
44
|
+
function opened(frame) {
|
|
45
|
+
const document = parseDiscovery(frame);
|
|
46
|
+
if (document === null)
|
|
47
|
+
return null;
|
|
48
|
+
bounds = document.bounds;
|
|
49
|
+
tracked.clear();
|
|
50
|
+
lister.forget();
|
|
51
|
+
for (const entry of trackedFrom(document))
|
|
52
|
+
tracked.set(entry.locator, entry);
|
|
53
|
+
notifications.track([...tracked.values()]);
|
|
123
54
|
return {
|
|
124
|
-
...(
|
|
125
|
-
|
|
126
|
-
...(more ? { pageToken: JSON.stringify({ ...continued, next, seen }) } : {}),
|
|
127
|
-
...(sent.frame === null ? {} : { received: sent.frame }),
|
|
55
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
|
|
56
|
+
member: document.member,
|
|
128
57
|
};
|
|
129
58
|
}
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return UNCERTAIN_READ;
|
|
146
|
-
return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
|
|
59
|
+
function changed(frame) {
|
|
60
|
+
const change = parseAuthorityChange(frame);
|
|
61
|
+
if (change === null)
|
|
62
|
+
return null;
|
|
63
|
+
for (const locator of change.left)
|
|
64
|
+
tracked.delete(locator);
|
|
65
|
+
for (const entry of trackedFrom(change))
|
|
66
|
+
tracked.set(entry.locator, entry);
|
|
67
|
+
notifications.track([...tracked.values()]);
|
|
68
|
+
return {
|
|
69
|
+
kind: 'authority-changed',
|
|
70
|
+
rooms: change.authorities.map((authority) => discoveredRoom(authority)),
|
|
71
|
+
left: change.left,
|
|
72
|
+
member: change.member,
|
|
73
|
+
};
|
|
147
74
|
}
|
|
148
|
-
async function
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
return
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const answer = await refreshFrom(view, ask.fromRoomVersion);
|
|
157
|
-
if (answer.kind !== 'net-advancement')
|
|
158
|
-
return answer;
|
|
159
|
-
handed.set(ask.entry.locator, roomVersion(view));
|
|
160
|
-
const models = await viewModels(view, ask.entry);
|
|
161
|
-
return models === undefined ? answer : { ...answer, models };
|
|
75
|
+
async function listedRoom(entry) {
|
|
76
|
+
const listed = await lister.list(entry);
|
|
77
|
+
if ('kind' in listed)
|
|
78
|
+
return listed;
|
|
79
|
+
const head = await chunkedHead(listed, entry, spaces);
|
|
80
|
+
if (head.kind === 'bootstrap')
|
|
81
|
+
chunked.set(entry.locator, listed);
|
|
82
|
+
return head;
|
|
162
83
|
}
|
|
163
|
-
async function
|
|
164
|
-
|
|
84
|
+
async function chunkRead(entry, id) {
|
|
85
|
+
const room = chunked.get(entry.locator);
|
|
86
|
+
const address = room?.chunks.get(id);
|
|
87
|
+
if (room === undefined || address === undefined)
|
|
165
88
|
return null;
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (advanced === null)
|
|
89
|
+
const read = await transport.download(address.url);
|
|
90
|
+
if (read.status !== EXPIRED_ADDRESS)
|
|
91
|
+
return { room, read };
|
|
92
|
+
const renewed = await renewal(entry, room);
|
|
93
|
+
if ('kind' in renewed || !renewed.chunks.has(id))
|
|
172
94
|
return null;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
95
|
+
chunked.set(entry.locator, renewed);
|
|
96
|
+
return { room: renewed, read: await transport.download(renewed.chunks.get(id).url) };
|
|
176
97
|
}
|
|
177
|
-
function
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
notifications.reach(entry.authority, at);
|
|
185
|
-
return [presented(entry.authority, at)];
|
|
186
|
-
});
|
|
187
|
-
notifications.send(kept);
|
|
98
|
+
function renewal(entry, stale) {
|
|
99
|
+
const held = renewals.get(stale);
|
|
100
|
+
if (held !== undefined)
|
|
101
|
+
return held;
|
|
102
|
+
const renewed = lister.renew(entry, [...chunked.keys()].flatMap((locator) => tracked.get(locator) ?? []));
|
|
103
|
+
renewals.set(stale, renewed);
|
|
104
|
+
return renewed;
|
|
188
105
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
106
|
+
function hold(rooms) {
|
|
107
|
+
for (const room of rooms) {
|
|
108
|
+
const listed = chunked.get(room.locator);
|
|
109
|
+
if (listed !== undefined &&
|
|
110
|
+
room.roomVersion !== null &&
|
|
111
|
+
reachedVersion(room.roomVersion, roomVersion(listed), listed.logEpoch))
|
|
112
|
+
chunked.delete(room.locator);
|
|
192
113
|
}
|
|
193
|
-
|
|
114
|
+
notifications.send(rooms);
|
|
194
115
|
}
|
|
195
|
-
async function
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return outcome;
|
|
201
|
-
const follows = followsVersion(held, outcome.roomVersion);
|
|
202
|
-
if (follows)
|
|
203
|
-
handed.set(entry.locator, outcome.roomVersion);
|
|
204
|
-
const at = logPosition(outcome.roomVersion);
|
|
205
|
-
if (follows && at !== null)
|
|
206
|
-
notifications.reach(entry.authority, at);
|
|
207
|
-
return outcome;
|
|
116
|
+
async function chunkPage(entry, id, arriving) {
|
|
117
|
+
const fetched = await chunkRead(entry, id);
|
|
118
|
+
if (fetched === null || fetched.read.status !== 200)
|
|
119
|
+
return UNCERTAIN_READ;
|
|
120
|
+
return chunkPart(fetched.read.body, fetched.room, id, arriving);
|
|
208
121
|
}
|
|
209
122
|
function authorityFor(locator) {
|
|
210
123
|
return tracked.get(locator);
|
|
@@ -223,116 +136,18 @@ export function createAgentsHouseAdapter(options) {
|
|
|
223
136
|
return result;
|
|
224
137
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
225
138
|
}
|
|
226
|
-
async function holding(asks) {
|
|
227
|
-
const bound = Math.floor(declared().max_held_files / Math.max(1, asks.filter((ask) => ask.held !== undefined).length));
|
|
228
|
-
const held = [];
|
|
229
|
-
for (const ask of asks) {
|
|
230
|
-
if (ask.held === undefined || bound === 0)
|
|
231
|
-
continue;
|
|
232
|
-
const files = await heldFiles(ask.entry.authority, ask.held, bound);
|
|
233
|
-
if (files !== null)
|
|
234
|
-
held.push(files);
|
|
235
|
-
}
|
|
236
|
-
return held;
|
|
237
|
-
}
|
|
238
|
-
async function bootstrapped(asks, answer) {
|
|
239
|
-
let open = asks.map((asked, ask) => ({ entry: asked.entry, asked, ask }));
|
|
240
|
-
while (open.length > 0) {
|
|
241
|
-
const unreached = [];
|
|
242
|
-
for (const part of selections(open, declared().max_prefixes)) {
|
|
243
|
-
const held = await holding(part.map(({ asked }) => asked));
|
|
244
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
245
|
-
prefixes: part.map(({ entry }) => authorityRoot(entry.handle)),
|
|
246
|
-
...(held.length === 0 ? {} : { held }),
|
|
247
|
-
});
|
|
248
|
-
const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
|
|
249
|
-
const parted = classified[0] === null ? parseParted(answered.body) : null;
|
|
250
|
-
for (const [index, asked] of part.entries()) {
|
|
251
|
-
const failure = classified[index];
|
|
252
|
-
if (failure !== null && failure !== undefined) {
|
|
253
|
-
answer(asked.ask, failure);
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
if (parted === null) {
|
|
257
|
-
answer(asked.ask, UNCERTAIN_READ);
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
const room = parted.rooms.find((candidate) => candidate.anchor === asked.entry.authority);
|
|
261
|
-
if (room === undefined && parted.next !== undefined) {
|
|
262
|
-
unreached.push(asked);
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
if (room === undefined) {
|
|
266
|
-
answer(asked.ask, { kind: 'room-not-readable', locator: asked.entry.locator });
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
const kept = held.find((files) => files.room === asked.entry.authority);
|
|
270
|
-
answer(asked.ask, await manifestPage(asked.asked, {
|
|
271
|
-
room,
|
|
272
|
-
next: 0,
|
|
273
|
-
seen: [],
|
|
274
|
-
...(kept === undefined ? {} : { held: kept }),
|
|
275
|
-
}));
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
open = unreached;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function layered(asks) {
|
|
282
|
-
const depths = new Map();
|
|
283
|
-
const layers = [];
|
|
284
|
-
for (const ask of asks) {
|
|
285
|
-
const depth = depths.get(ask.ask.entry.authority) ?? 0;
|
|
286
|
-
depths.set(ask.ask.entry.authority, depth + 1);
|
|
287
|
-
(layers[depth] ??= []).push(ask);
|
|
288
|
-
}
|
|
289
|
-
return layers;
|
|
290
|
-
}
|
|
291
|
-
async function enumerated(asks) {
|
|
292
|
-
const answers = [];
|
|
293
|
-
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, declared().max_prefixes));
|
|
294
|
-
for (const part of parts) {
|
|
295
|
-
let open = [...part];
|
|
296
|
-
while (open.length > 0) {
|
|
297
|
-
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
298
|
-
prefixes: open.map(({ ask }) => authorityRoot(ask.entry.handle)),
|
|
299
|
-
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
300
|
-
});
|
|
301
|
-
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
302
|
-
if (classified[0]?.kind === 'beyond-horizon') {
|
|
303
|
-
const named = doorRefusal(answer.body)?.fields.authority;
|
|
304
|
-
const expired = open.filter(({ ask }) => ask.entry.authority === named);
|
|
305
|
-
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
306
|
-
const gone = expired.length === 0 ? open : expired;
|
|
307
|
-
for (const ask of gone)
|
|
308
|
-
answers[ask.index] = answered;
|
|
309
|
-
open = open.filter((ask) => !gone.includes(ask));
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
313
|
-
for (const [answerIndex, { ask, index }] of open.entries()) {
|
|
314
|
-
const failure = classified[answerIndex];
|
|
315
|
-
if (failure !== null && failure !== undefined) {
|
|
316
|
-
answers[index] = failure;
|
|
317
|
-
continue;
|
|
318
|
-
}
|
|
319
|
-
answers[index] =
|
|
320
|
-
enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
|
|
321
|
-
}
|
|
322
|
-
open = [];
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return answers;
|
|
326
|
-
}
|
|
327
|
-
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
328
|
-
const enumerateTogether = gathered(enumerated);
|
|
329
139
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
330
140
|
const paths = batchPaths(changes);
|
|
331
141
|
const sha256 = await deltaDigest(delta);
|
|
332
142
|
if (handed !== undefined && handed.sha256 === sha256) {
|
|
333
143
|
return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
|
|
334
144
|
}
|
|
335
|
-
const minted = await transport.door(
|
|
145
|
+
const minted = await transport.door(houseDoorRoute('files/batch-upload'), {
|
|
146
|
+
...head,
|
|
147
|
+
paths,
|
|
148
|
+
bytes,
|
|
149
|
+
sha256,
|
|
150
|
+
});
|
|
336
151
|
if (minted.status !== 200) {
|
|
337
152
|
return {
|
|
338
153
|
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
@@ -380,11 +195,13 @@ export function createAgentsHouseAdapter(options) {
|
|
|
380
195
|
: { body: inline };
|
|
381
196
|
if ('outcome' in prepared)
|
|
382
197
|
return prepared.outcome;
|
|
383
|
-
const answer = await transport.door(
|
|
198
|
+
const answer = await transport.door(houseDoorRoute('files/batch'), prepared.body);
|
|
384
199
|
const stagedFiles = await staged(answer, request);
|
|
385
200
|
if (stagedFiles === false)
|
|
386
201
|
return UNCERTAIN;
|
|
387
|
-
const settled = stagedFiles === null
|
|
202
|
+
const settled = stagedFiles === null
|
|
203
|
+
? answer
|
|
204
|
+
: await transport.door(houseDoorRoute('files/batch'), prepared.body);
|
|
388
205
|
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
389
206
|
return UNCERTAIN;
|
|
390
207
|
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
@@ -412,9 +229,22 @@ export function createAgentsHouseAdapter(options) {
|
|
|
412
229
|
function linked(answer, locator) {
|
|
413
230
|
const address = addressed(answer, locator);
|
|
414
231
|
return address.kind === 'address'
|
|
415
|
-
? { kind: 'link', url: address.url, name: address.name }
|
|
232
|
+
? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
|
|
416
233
|
: address;
|
|
417
234
|
}
|
|
235
|
+
function refusedAttach(answer, locator) {
|
|
236
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
237
|
+
if (failure === null)
|
|
238
|
+
return null;
|
|
239
|
+
return failure.kind === 'scoped-refusal'
|
|
240
|
+
? {
|
|
241
|
+
kind: 'rejected',
|
|
242
|
+
code: failure.code,
|
|
243
|
+
fields: failure.fields,
|
|
244
|
+
retryable: failure.retryable,
|
|
245
|
+
}
|
|
246
|
+
: failure;
|
|
247
|
+
}
|
|
418
248
|
async function downloaded(ask, locator) {
|
|
419
249
|
let address = addressed(await ask(), locator);
|
|
420
250
|
if (address.kind !== 'address')
|
|
@@ -439,7 +269,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
439
269
|
const entry = authorityFor(request.locator);
|
|
440
270
|
if (entry === undefined)
|
|
441
271
|
return null;
|
|
442
|
-
return () => transport.door(
|
|
272
|
+
return () => transport.door(houseDoorRoute('files/read'), {
|
|
443
273
|
room_ref: entry.authority,
|
|
444
274
|
path: request.path,
|
|
445
275
|
revision: request.revision,
|
|
@@ -447,72 +277,20 @@ export function createAgentsHouseAdapter(options) {
|
|
|
447
277
|
}
|
|
448
278
|
return {
|
|
449
279
|
roomTypes: HOUSE_ROOM_TYPES,
|
|
280
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
281
|
+
typedPaths: HOUSE_TYPED_PATHS,
|
|
282
|
+
unkeptReads: HOUSE_UNKEPT_READS,
|
|
283
|
+
leadingReads: HOUSE_LEADING_READS,
|
|
450
284
|
async authorize() {
|
|
451
285
|
return options.authorize();
|
|
452
286
|
},
|
|
453
|
-
async discover(_authorization, scope) {
|
|
454
|
-
const answer = await transport.door(DOOR_DISCOVER, {
|
|
455
|
-
prefixes: [...DISCOVER_PREFIXES],
|
|
456
|
-
...(scope === undefined ? {} : { rooms: scope.authorities }),
|
|
457
|
-
});
|
|
458
|
-
const failed = discoveryFailure(answer.status, answer.body);
|
|
459
|
-
if (failed !== null)
|
|
460
|
-
return failed;
|
|
461
|
-
const document = parseDiscovery(answer.body);
|
|
462
|
-
if (document === null)
|
|
463
|
-
return { kind: 'transport-uncertainty' };
|
|
464
|
-
bounds = document.bounds;
|
|
465
|
-
const entries = trackedFrom(document);
|
|
466
|
-
const covered = scope === undefined
|
|
467
|
-
? null
|
|
468
|
-
: [
|
|
469
|
-
...new Set([
|
|
470
|
-
...[...tracked.values()]
|
|
471
|
-
.filter((entry) => scope.authorities.includes(entry.authority))
|
|
472
|
-
.map((entry) => entry.locator),
|
|
473
|
-
...entries.map((entry) => entry.locator),
|
|
474
|
-
]),
|
|
475
|
-
];
|
|
476
|
-
if (covered === null)
|
|
477
|
-
tracked.clear();
|
|
478
|
-
else
|
|
479
|
-
for (const locator of covered)
|
|
480
|
-
tracked.delete(locator);
|
|
481
|
-
for (const entry of entries)
|
|
482
|
-
tracked.set(entry.locator, entry);
|
|
483
|
-
for (const locator of handed.keys())
|
|
484
|
-
if (!tracked.has(locator))
|
|
485
|
-
handed.delete(locator);
|
|
486
|
-
notifications.track([...tracked.values()]);
|
|
487
|
-
return {
|
|
488
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions, [...tracked.values()])),
|
|
489
|
-
member: document.member,
|
|
490
|
-
...(covered === null ? {} : { covered }),
|
|
491
|
-
};
|
|
492
|
-
},
|
|
493
287
|
async bootstrap(_authorization, request) {
|
|
494
288
|
const entry = authorityFor(request.locator);
|
|
495
289
|
if (entry === undefined)
|
|
496
290
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
return bootstrapTogether(asked);
|
|
502
|
-
},
|
|
503
|
-
async refresh(_authorization, request) {
|
|
504
|
-
const entry = authorityFor(request.locator);
|
|
505
|
-
if (entry === undefined)
|
|
506
|
-
return { kind: 'room-not-readable', locator: request.locator };
|
|
507
|
-
if (request.pageToken !== undefined)
|
|
508
|
-
return { kind: 'transport-uncertainty' };
|
|
509
|
-
const from = logPosition(request.fromRoomVersion);
|
|
510
|
-
if (from === null)
|
|
511
|
-
return { kind: 'beyond-horizon' };
|
|
512
|
-
const answered = await streamed(entry, request, from);
|
|
513
|
-
if (answered !== null)
|
|
514
|
-
return answered;
|
|
515
|
-
return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
|
|
291
|
+
return request.pageToken === undefined
|
|
292
|
+
? listedRoom(entry)
|
|
293
|
+
: chunkPage(entry, request.pageToken, request.arriving);
|
|
516
294
|
},
|
|
517
295
|
async mutate(_authorization, request, keep) {
|
|
518
296
|
const entry = authorityFor(request.locator);
|
|
@@ -529,22 +307,21 @@ export function createAgentsHouseAdapter(options) {
|
|
|
529
307
|
: houseMove(batch, entry.handle, into.handle);
|
|
530
308
|
if (mapped.kind === 'unmapped')
|
|
531
309
|
return rejectedMapping(mapped);
|
|
532
|
-
const head = operationHead(batch);
|
|
310
|
+
const head = operationHead(batch, entry.authority);
|
|
533
311
|
const delta = deltaBody(mapped.changes);
|
|
534
312
|
const bytes = deltaBytes(delta);
|
|
535
313
|
const handed = request.upload;
|
|
536
314
|
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
537
|
-
const settle = (outcome) => into === undefined ? adopted(entry, outcome) : adoptedMove(entry, into, outcome);
|
|
538
315
|
const outcome = await answer(handed);
|
|
539
316
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
540
|
-
return
|
|
541
|
-
return
|
|
317
|
+
return outcome;
|
|
318
|
+
return answer(undefined);
|
|
542
319
|
},
|
|
543
320
|
async history(_authorization, request) {
|
|
544
321
|
const entry = authorityFor(request.locator);
|
|
545
322
|
if (entry === undefined)
|
|
546
323
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
547
|
-
const answer = await transport.door(
|
|
324
|
+
const answer = await transport.door(houseDoorRoute('files/revisions/list'), revisionBody({
|
|
548
325
|
path: request.path,
|
|
549
326
|
handle: entry.handle,
|
|
550
327
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
@@ -567,6 +344,35 @@ export function createAgentsHouseAdapter(options) {
|
|
|
567
344
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
568
345
|
return linked(await ask(), request.locator);
|
|
569
346
|
},
|
|
347
|
+
async attach(_authorization, request) {
|
|
348
|
+
const entry = authorityFor(request.locator);
|
|
349
|
+
if (entry === undefined)
|
|
350
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
351
|
+
const declared = await transport.door(houseDoorRoute('files/attach'), {
|
|
352
|
+
room_ref: entry.authority,
|
|
353
|
+
name: request.name,
|
|
354
|
+
bytes: request.byteLength,
|
|
355
|
+
sha256: request.sha256,
|
|
356
|
+
media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
|
|
357
|
+
});
|
|
358
|
+
const refused = refusedAttach(declared, request.locator);
|
|
359
|
+
if (refused !== null)
|
|
360
|
+
return refused;
|
|
361
|
+
const upload = houseUpload(declared.body?.upload);
|
|
362
|
+
if (upload === null)
|
|
363
|
+
return UNCERTAIN_READ;
|
|
364
|
+
const finished = await finishedUpload(transport, upload, request.blob);
|
|
365
|
+
if (finished === null)
|
|
366
|
+
return UNCERTAIN_READ;
|
|
367
|
+
const unfinished = refusedAttach(finished, request.locator);
|
|
368
|
+
if (unfinished !== null)
|
|
369
|
+
return unfinished;
|
|
370
|
+
const { path, revision } = (finished.body ?? {});
|
|
371
|
+
const root = `/rooms/${entry.handle}/`;
|
|
372
|
+
return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
|
|
373
|
+
? { kind: 'attached', path: path.slice(root.length), revision }
|
|
374
|
+
: UNCERTAIN_READ;
|
|
375
|
+
},
|
|
570
376
|
reached(locator, version, target) {
|
|
571
377
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
572
378
|
},
|
|
@@ -576,18 +382,12 @@ export function createAgentsHouseAdapter(options) {
|
|
|
576
382
|
async action(_authorization, request) {
|
|
577
383
|
return withAt(await houseAction(transport, request));
|
|
578
384
|
},
|
|
385
|
+
search(_authorization, request) {
|
|
386
|
+
return houseSearch(transport, [...tracked.values()], request);
|
|
387
|
+
},
|
|
579
388
|
roomStream: {
|
|
580
|
-
hold
|
|
581
|
-
|
|
582
|
-
observe(notify, receipts, revisions) {
|
|
583
|
-
heldRevisions = revisions;
|
|
584
|
-
return notifications.observe(notify, receipts);
|
|
585
|
-
},
|
|
586
|
-
published(locator, roomVersion) {
|
|
587
|
-
const at = logPosition(roomVersion);
|
|
588
|
-
if (at !== null)
|
|
589
|
-
notifications.published(locator, at);
|
|
590
|
-
},
|
|
389
|
+
hold,
|
|
390
|
+
observe: notifications.observe,
|
|
591
391
|
},
|
|
592
392
|
conversations: notifications.conversations,
|
|
593
393
|
serviceMode: notifications.serviceMode,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-reference.d.ts","sourceRoot":"","sources":["../src/app-reference.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,qBAAqB,QAEjC,CAAC"}
|