@agentshouse/mdruntime-adapter 0.1.0-cedb8e57837a7 → 0.1.0-cf059d0c8e9c0
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 +3 -68
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +1 -355
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +196 -410
- 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/crockford.d.ts +4 -0
- package/dist/crockford.d.ts.map +1 -0
- package/dist/crockford.js +3 -0
- package/dist/discovery.d.ts +12 -12
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +44 -51
- package/dist/history.d.ts +12 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +91 -0
- package/dist/house-model.d.ts +2249 -0
- package/dist/house-model.d.ts.map +1 -0
- package/dist/house-model.js +1237 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/model.d.ts +5 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +10 -29
- package/dist/part.d.ts +14 -0
- package/dist/part.d.ts.map +1 -0
- package/dist/part.js +90 -0
- package/dist/read.d.ts +27 -59
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +167 -266
- package/dist/refusal.d.ts +0 -3
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +0 -5
- package/dist/room-stream.d.ts +19 -0
- package/dist/room-stream.d.ts.map +1 -0
- package/dist/room-stream.js +57 -0
- package/dist/room-types.d.ts +7 -0
- package/dist/room-types.d.ts.map +1 -0
- package/dist/room-types.js +126 -0
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +81 -193
- package/dist/stream.d.ts +13 -13
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +161 -409
- package/dist/transport.d.ts +49 -39
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +53 -11
- package/dist/upload.d.ts +12 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +63 -0
- package/dist/write.d.ts +4 -0
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +22 -18
- package/package.json +8 -4
- package/AGENTS.md +0 -304
- package/dist/gather.d.ts +0 -4
- package/dist/gather.d.ts.map +0 -1
- package/dist/gather.js +0 -35
- package/dist/handed.d.ts +0 -15
- package/dist/handed.d.ts.map +0 -1
- package/dist/handed.js +0 -45
- package/dist/referrer.d.ts +0 -7
- package/dist/referrer.d.ts.map +0 -1
- package/dist/referrer.js +0 -35
package/dist/adapter.js
CHANGED
|
@@ -1,67 +1,47 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { partModels, streamedModels, viewModels } from './model.js';
|
|
4
|
-
import { gathered, gatheredEach, selections } from './gather.js';
|
|
1
|
+
import { stepped } from '@agentshouse/mdruntime';
|
|
2
|
+
import { capabilities, discoveredRoom, mapDiscovery, parseAuthorityChange, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
5
3
|
import { createSiteAdapter } from './site.js';
|
|
6
|
-
import { authorityRoot, bootstrapping,
|
|
4
|
+
import { authorityRoot, bootstrapping, parseFileAddress, parseGrant, } from './read.js';
|
|
5
|
+
import { roomStreams } from './room-stream.js';
|
|
7
6
|
import { sha256Hex } from './digest.js';
|
|
8
|
-
import {
|
|
7
|
+
import { finishedUpload, houseUpload } from './upload.js';
|
|
9
8
|
import { houseAction } from './actions.js';
|
|
10
|
-
import { classifyDoor,
|
|
11
|
-
import { houseNotifications } from './stream.js';
|
|
12
|
-
import {
|
|
9
|
+
import { classifyDoor, doorRefusal } from './refusal.js';
|
|
10
|
+
import { houseNotifications, } from './stream.js';
|
|
11
|
+
import { houseDoorRoute, } from './transport.js';
|
|
13
12
|
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
14
|
-
import {
|
|
15
|
-
import { followsVersion, logPosition, presented, reachedVersion, roomVersion
|
|
13
|
+
import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
|
|
14
|
+
import { followsVersion, logPosition, presented, reachedVersion, roomVersion } from './version.js';
|
|
16
15
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
17
16
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
17
|
+
const OCTET_STREAM = 'application/octet-stream';
|
|
18
|
+
const EXPIRED_ADDRESS = 403;
|
|
18
19
|
function lapsedGrant(outcome) {
|
|
19
20
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
20
21
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
return known;
|
|
26
|
-
const sorted = await stepped(sortedKeys(held.documents));
|
|
27
|
-
heldPaths.set(held.documents, sorted);
|
|
28
|
-
return sorted;
|
|
29
|
-
}
|
|
30
|
-
function heldFiles(authority, held, paths, after, bound) {
|
|
31
|
-
let low = 0;
|
|
32
|
-
for (let high = paths.length; low < high;) {
|
|
33
|
-
const middle = (low + high) >>> 1;
|
|
34
|
-
if (after !== null && paths[middle] <= after)
|
|
35
|
-
low = middle + 1;
|
|
36
|
-
else
|
|
37
|
-
high = middle;
|
|
38
|
-
}
|
|
39
|
-
const files = [];
|
|
40
|
-
let through = null;
|
|
41
|
-
for (let at = low; at < paths.length && through === null; at += 1) {
|
|
42
|
-
const revision = held.revisions[paths[at]];
|
|
43
|
-
if (revision === undefined)
|
|
44
|
-
continue;
|
|
45
|
-
if (files.length === bound)
|
|
46
|
-
through = files.at(-1).path;
|
|
47
|
-
else
|
|
48
|
-
files.push({ path: paths[at], revision });
|
|
49
|
-
}
|
|
22
|
+
async function heldFiles(authority, held, bound) {
|
|
23
|
+
const listed = await held.files(null, bound + 1);
|
|
24
|
+
const files = listed.slice(0, bound);
|
|
25
|
+
const through = listed.length > bound ? (files.at(-1)?.path ?? null) : null;
|
|
50
26
|
return files.length === 0 ? null : { room: authority, through, files };
|
|
51
27
|
}
|
|
52
28
|
async function reused(scope, held) {
|
|
53
29
|
const sent = new Set(scope.contents.map((entry) => entry.path));
|
|
54
30
|
const contents = [...scope.contents];
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
31
|
+
const asked = scope.files.filter((file) => file.binary === undefined && !sent.has(file.path));
|
|
32
|
+
if (asked.length === 0)
|
|
33
|
+
return scope;
|
|
34
|
+
if (held === undefined)
|
|
35
|
+
return null;
|
|
36
|
+
const kept = await held.contents(asked.map(({ path }) => path));
|
|
37
|
+
for (const file of asked) {
|
|
38
|
+
const found = kept.get(file.path);
|
|
39
|
+
if (found === undefined ||
|
|
40
|
+
found.revision !== file.revision ||
|
|
41
|
+
(await sha256Hex(found.content)) !== file.sha256) {
|
|
62
42
|
return null;
|
|
63
43
|
}
|
|
64
|
-
contents.push({ path: file.path, content });
|
|
44
|
+
contents.push({ path: file.path, content: found.content });
|
|
65
45
|
}
|
|
66
46
|
return { ...scope, contents };
|
|
67
47
|
}
|
|
@@ -69,136 +49,84 @@ export function createAgentsHouseAdapter(options) {
|
|
|
69
49
|
if ('site' in options)
|
|
70
50
|
return createSiteAdapter(options.site, options.snapshot === true);
|
|
71
51
|
const transport = options.transport;
|
|
72
|
-
const notifications = houseNotifications(transport, 'credential'
|
|
52
|
+
const notifications = houseNotifications(transport, 'credential', {
|
|
53
|
+
opened: async (frame) => (frame === null ? null : opened(frame)),
|
|
54
|
+
changed,
|
|
55
|
+
});
|
|
73
56
|
const tracked = new Map();
|
|
74
|
-
const
|
|
57
|
+
const rooms = roomStreams('credential');
|
|
58
|
+
const unreused = new Set();
|
|
75
59
|
let bounds;
|
|
76
60
|
function declared() {
|
|
77
61
|
if (bounds === undefined)
|
|
78
62
|
throw new Error('no Discovery declared the bounds');
|
|
79
63
|
return bounds;
|
|
80
64
|
}
|
|
81
|
-
function
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
65
|
+
function opened(frame) {
|
|
66
|
+
const document = parseDiscovery(frame);
|
|
67
|
+
if (document === null)
|
|
68
|
+
return null;
|
|
69
|
+
bounds = document.bounds;
|
|
70
|
+
tracked.clear();
|
|
71
|
+
for (const entry of trackedFrom(document))
|
|
72
|
+
tracked.set(entry.locator, entry);
|
|
73
|
+
notifications.track([...tracked.values()]);
|
|
74
|
+
return {
|
|
75
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
|
|
76
|
+
member: document.member,
|
|
77
|
+
};
|
|
94
78
|
}
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
98
|
-
return
|
|
99
|
-
const
|
|
79
|
+
function changed(frame) {
|
|
80
|
+
const change = parseAuthorityChange(frame);
|
|
81
|
+
if (change === null)
|
|
82
|
+
return null;
|
|
83
|
+
for (const locator of change.left)
|
|
84
|
+
tracked.delete(locator);
|
|
85
|
+
for (const entry of trackedFrom(change))
|
|
86
|
+
tracked.set(entry.locator, entry);
|
|
87
|
+
notifications.track([...tracked.values()]);
|
|
88
|
+
return {
|
|
89
|
+
kind: 'authority-changed',
|
|
90
|
+
rooms: change.authorities.map((authority) => discoveredRoom(authority)),
|
|
91
|
+
left: change.left,
|
|
92
|
+
member: change.member,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function streamedPage(entry, token, held) {
|
|
96
|
+
const part = await rooms.part(entry, token);
|
|
97
|
+
if ('kind' in part)
|
|
98
|
+
return part;
|
|
99
|
+
const scope = await reused(part.scope, held);
|
|
100
100
|
if (scope === null) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
: continued === null
|
|
104
|
-
? bootstrapTogether({ entry, held: undefined })
|
|
105
|
-
: continuedBootstrap({ entry, held: undefined }, continued);
|
|
101
|
+
unreused.add(entry.locator);
|
|
102
|
+
return UNCERTAIN_READ;
|
|
106
103
|
}
|
|
107
|
-
const cursor = envelope.next?.room === entry.authority ? envelope.next : undefined;
|
|
108
|
-
const next = cursor?.path ?? null;
|
|
109
|
-
if (continued === null && next === null)
|
|
110
|
-
await hand(entry.locator, scopeHanded(scope));
|
|
111
|
-
else
|
|
112
|
-
handed.delete(entry.locator);
|
|
113
|
-
notifications.reach(entry.authority, scope);
|
|
114
|
-
const seen = [
|
|
115
|
-
...(continued?.seen ?? []),
|
|
116
|
-
...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
|
|
117
|
-
];
|
|
118
|
-
const models = await partModels(scope, entry, new Set(seen), next === null);
|
|
119
|
-
const continuation = next === null
|
|
120
|
-
? null
|
|
121
|
-
: { after: next, seen, ...(cursor?.cut === undefined ? {} : { cut: cursor.cut }) };
|
|
122
104
|
return {
|
|
123
105
|
...(await stepped(bootstrapping(scope))),
|
|
124
|
-
...(models === undefined ? {} : { models }),
|
|
125
|
-
...(
|
|
106
|
+
...(part.models === undefined ? {} : { models: part.models }),
|
|
107
|
+
...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
|
|
108
|
+
bytes: part.bytes,
|
|
126
109
|
};
|
|
127
110
|
}
|
|
128
|
-
async function
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return failure;
|
|
141
|
-
const enumeration = parseEnumeration(answer.body, 'credential');
|
|
142
|
-
if (enumeration === null)
|
|
143
|
-
return UNCERTAIN_READ;
|
|
144
|
-
return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
|
|
145
|
-
}
|
|
146
|
-
async function enumeratedRoom(enumeration, ask) {
|
|
147
|
-
const first = viewOf(enumeration, ask.entry);
|
|
148
|
-
if (first === undefined)
|
|
149
|
-
return { kind: 'room-not-readable', locator: ask.entry.locator };
|
|
150
|
-
const view = await wholeView(first, (page) => enumeratedPage(ask, page));
|
|
151
|
-
if ('kind' in view)
|
|
152
|
-
return view;
|
|
153
|
-
notifications.reach(ask.entry.authority, view);
|
|
154
|
-
const answer = await refreshFrom(view, ask.fromRoomVersion);
|
|
155
|
-
if (answer.kind !== 'net-advancement')
|
|
156
|
-
return answer;
|
|
157
|
-
await hand(ask.entry.locator, enumeratedHanded(view, handed.get(ask.entry.locator)));
|
|
158
|
-
const models = await viewModels(view, ask.entry);
|
|
159
|
-
return models === undefined ? answer : { ...answer, models };
|
|
160
|
-
}
|
|
161
|
-
async function streamed(entry, request, from) {
|
|
162
|
-
const held = handed.get(entry.locator);
|
|
163
|
-
if (held?.roomVersion !== request.fromRoomVersion)
|
|
164
|
-
return null;
|
|
165
|
-
const advanced = await stepped(notifications.advance(entry, from, (path, revision) => held.revisions[path] === revision));
|
|
166
|
-
if (advanced === null)
|
|
167
|
-
return null;
|
|
168
|
-
await hand(entry.locator, streamedHanded(held, advanced));
|
|
169
|
-
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
170
|
-
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
111
|
+
async function openedRoom(entry, request) {
|
|
112
|
+
const reusing = unreused.delete(entry.locator) ? undefined : request.held;
|
|
113
|
+
const files = reusing === undefined
|
|
114
|
+
? null
|
|
115
|
+
: await heldFiles(entry.authority, reusing, declared().max_held_files);
|
|
116
|
+
const answered = await transport.streamed(houseDoorRoute('bootstrap'), {
|
|
117
|
+
prefixes: [authorityRoot(entry.handle)],
|
|
118
|
+
...(files === null ? {} : { held: [files] }),
|
|
119
|
+
}, request.signal ?? new AbortController().signal);
|
|
120
|
+
if (answered.room === null)
|
|
121
|
+
return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
|
|
122
|
+
return rooms.head(entry, answered.room, request.arriving, request.signal);
|
|
171
123
|
}
|
|
172
124
|
function held(rooms) {
|
|
173
|
-
|
|
125
|
+
notifications.send(rooms.flatMap((room) => {
|
|
174
126
|
const entry = tracked.get(room.locator);
|
|
175
127
|
const at = logPosition(room.roomVersion);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
handed.set(room.locator, heldHanded(room));
|
|
179
|
-
notifications.reach(entry.authority, at);
|
|
180
|
-
return [presented(entry.authority, at)];
|
|
181
|
-
});
|
|
182
|
-
notifications.send(kept);
|
|
183
|
-
}
|
|
184
|
-
async function adoptedMove(entry, into, outcome) {
|
|
185
|
-
if (outcome.kind === 'accepted' && outcome.destination !== undefined) {
|
|
186
|
-
await adopted(into, { kind: 'accepted', ...outcome.destination });
|
|
187
|
-
}
|
|
188
|
-
return adopted(entry, outcome);
|
|
189
|
-
}
|
|
190
|
-
async function adopted(entry, outcome) {
|
|
191
|
-
if (outcome.kind !== 'accepted')
|
|
192
|
-
return outcome;
|
|
193
|
-
const held = handed.get(entry.locator);
|
|
194
|
-
if (held === undefined)
|
|
195
|
-
return outcome;
|
|
196
|
-
const follows = followsVersion(held.roomVersion, outcome.roomVersion);
|
|
197
|
-
await hand(entry.locator, acceptedHanded(held, outcome, follows ? outcome.roomVersion : held.roomVersion));
|
|
198
|
-
const at = logPosition(outcome.roomVersion);
|
|
199
|
-
if (follows && at !== null)
|
|
200
|
-
notifications.reach(entry.authority, at);
|
|
201
|
-
return outcome;
|
|
128
|
+
return entry === undefined || at === null ? [] : [presented(entry.authority, at)];
|
|
129
|
+
}));
|
|
202
130
|
}
|
|
203
131
|
function authorityFor(locator) {
|
|
204
132
|
return tracked.get(locator);
|
|
@@ -217,136 +145,25 @@ export function createAgentsHouseAdapter(options) {
|
|
|
217
145
|
return result;
|
|
218
146
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
219
147
|
}
|
|
220
|
-
async function holding(asks, after) {
|
|
221
|
-
const bound = Math.floor(declared().max_held_files / Math.max(1, asks.filter((ask) => ask.held !== undefined).length));
|
|
222
|
-
const held = [];
|
|
223
|
-
for (const ask of asks) {
|
|
224
|
-
if (ask.held === undefined || bound === 0)
|
|
225
|
-
continue;
|
|
226
|
-
const paths = await sortedHeld(ask.held);
|
|
227
|
-
const files = heldFiles(ask.entry.authority, ask.held, paths, after, bound);
|
|
228
|
-
if (files !== null)
|
|
229
|
-
held.push(files);
|
|
230
|
-
}
|
|
231
|
-
return held;
|
|
232
|
-
}
|
|
233
|
-
async function bootstrapped(asks, answer) {
|
|
234
|
-
let open = asks.map((asked, ask) => ({ entry: asked.entry, asked, ask }));
|
|
235
|
-
while (open.length > 0) {
|
|
236
|
-
const unreached = [];
|
|
237
|
-
for (const part of selections(open, declared().max_prefixes)) {
|
|
238
|
-
const held = await holding(part.map(({ asked }) => asked), null);
|
|
239
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
240
|
-
prefixes: part.map(({ entry }) => authorityRoot(entry.handle)),
|
|
241
|
-
...(held.length === 0 ? {} : { held }),
|
|
242
|
-
});
|
|
243
|
-
const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
|
|
244
|
-
const envelope = classified[0] === null ? await stepped(parseEnvelope(answered.body)) : null;
|
|
245
|
-
for (const [index, asked] of part.entries()) {
|
|
246
|
-
const failure = classified[index];
|
|
247
|
-
if (failure !== null && failure !== undefined) {
|
|
248
|
-
answer(asked.ask, failure);
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
if (envelope === null) {
|
|
252
|
-
answer(asked.ask, UNCERTAIN_READ);
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, asked.entry));
|
|
256
|
-
if (scope === undefined && envelope.next !== undefined) {
|
|
257
|
-
unreached.push(asked);
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.asked, null));
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
open = unreached;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
async function continuedBootstrap(asked, continued) {
|
|
267
|
-
const { entry } = asked;
|
|
268
|
-
const after = {
|
|
269
|
-
room: entry.authority,
|
|
270
|
-
path: continued.after,
|
|
271
|
-
...(continued.cut === undefined ? {} : { cut: continued.cut }),
|
|
272
|
-
};
|
|
273
|
-
const held = await holding([asked], continued.after);
|
|
274
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
275
|
-
prefixes: [authorityRoot(entry.handle)],
|
|
276
|
-
after,
|
|
277
|
-
...(held.length === 0 ? {} : { held }),
|
|
278
|
-
});
|
|
279
|
-
const failure = classifyRead(answered.status, answered.body, entry.locator);
|
|
280
|
-
if (failure !== null)
|
|
281
|
-
return failure;
|
|
282
|
-
const envelope = await stepped(parseEnvelope(answered.body));
|
|
283
|
-
if (envelope === null)
|
|
284
|
-
return UNCERTAIN_READ;
|
|
285
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, entry));
|
|
286
|
-
return bootstrappedRoom(envelope, scope, asked, continued);
|
|
287
|
-
}
|
|
288
|
-
function layered(asks) {
|
|
289
|
-
const depths = new Map();
|
|
290
|
-
const layers = [];
|
|
291
|
-
for (const ask of asks) {
|
|
292
|
-
const depth = depths.get(ask.ask.entry.authority) ?? 0;
|
|
293
|
-
depths.set(ask.ask.entry.authority, depth + 1);
|
|
294
|
-
(layers[depth] ??= []).push(ask);
|
|
295
|
-
}
|
|
296
|
-
return layers;
|
|
297
|
-
}
|
|
298
|
-
async function enumerated(asks) {
|
|
299
|
-
const answers = [];
|
|
300
|
-
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, declared().max_prefixes));
|
|
301
|
-
for (const part of parts) {
|
|
302
|
-
let open = [...part];
|
|
303
|
-
while (open.length > 0) {
|
|
304
|
-
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
305
|
-
prefixes: open.map(({ ask }) => authorityRoot(ask.entry.handle)),
|
|
306
|
-
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
307
|
-
});
|
|
308
|
-
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
309
|
-
if (classified[0]?.kind === 'beyond-horizon') {
|
|
310
|
-
const named = doorRefusal(answer.body)?.fields.authority;
|
|
311
|
-
const expired = open.filter(({ ask }) => ask.entry.authority === named);
|
|
312
|
-
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
313
|
-
const gone = expired.length === 0 ? open : expired;
|
|
314
|
-
for (const ask of gone)
|
|
315
|
-
answers[ask.index] = answered;
|
|
316
|
-
open = open.filter((ask) => !gone.includes(ask));
|
|
317
|
-
continue;
|
|
318
|
-
}
|
|
319
|
-
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
320
|
-
for (const [answerIndex, { ask, index }] of open.entries()) {
|
|
321
|
-
const failure = classified[answerIndex];
|
|
322
|
-
if (failure !== null && failure !== undefined) {
|
|
323
|
-
answers[index] = failure;
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
answers[index] =
|
|
327
|
-
enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
|
|
328
|
-
}
|
|
329
|
-
open = [];
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return answers;
|
|
333
|
-
}
|
|
334
|
-
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
335
|
-
const enumerateTogether = gathered(enumerated);
|
|
336
148
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
337
149
|
const paths = batchPaths(changes);
|
|
338
150
|
const sha256 = await deltaDigest(delta);
|
|
339
151
|
if (handed !== undefined && handed.sha256 === sha256) {
|
|
340
152
|
return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
|
|
341
153
|
}
|
|
342
|
-
const minted = await transport.door(
|
|
154
|
+
const minted = await transport.door(houseDoorRoute('batch-upload'), {
|
|
155
|
+
...head,
|
|
156
|
+
paths,
|
|
157
|
+
bytes,
|
|
158
|
+
sha256,
|
|
159
|
+
});
|
|
343
160
|
if (minted.status !== 200) {
|
|
344
161
|
return {
|
|
345
162
|
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
346
163
|
};
|
|
347
164
|
}
|
|
348
165
|
const grant = parseGrant(minted.body);
|
|
349
|
-
if (grant === null
|
|
166
|
+
if (grant === null)
|
|
350
167
|
return { outcome: UNCERTAIN };
|
|
351
168
|
const delivered = await transport.transfer({
|
|
352
169
|
act: 'authored_batch_upload',
|
|
@@ -356,48 +173,43 @@ export function createAgentsHouseAdapter(options) {
|
|
|
356
173
|
if (delivered.status !== 200)
|
|
357
174
|
return { outcome: UNCERTAIN };
|
|
358
175
|
const holding = parseGrant(delivered.body);
|
|
359
|
-
if (holding === null
|
|
176
|
+
if (holding === null)
|
|
360
177
|
return { outcome: UNCERTAIN };
|
|
361
178
|
const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
|
|
362
179
|
await keep(upload);
|
|
363
180
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
364
181
|
}
|
|
365
|
-
async function staged(answer, batch) {
|
|
182
|
+
async function staged(answer, { changeBatch: batch, file }) {
|
|
366
183
|
const refusal = doorRefusal(answer.body);
|
|
367
184
|
if (refusal?.code !== 'files_unstaged')
|
|
368
185
|
return null;
|
|
369
|
-
const
|
|
370
|
-
if (!Array.isArray(
|
|
186
|
+
const uploads = refusal.fields.upload;
|
|
187
|
+
if (!Array.isArray(uploads))
|
|
371
188
|
return false;
|
|
372
|
-
for (const
|
|
373
|
-
const
|
|
374
|
-
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === sha256);
|
|
375
|
-
if (
|
|
189
|
+
for (const named of uploads) {
|
|
190
|
+
const upload = houseUpload(named);
|
|
191
|
+
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
|
|
192
|
+
if (upload === null || change === undefined)
|
|
376
193
|
return false;
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
url,
|
|
380
|
-
body: change.blob,
|
|
381
|
-
operation,
|
|
382
|
-
});
|
|
383
|
-
if (delivered.status !== 200)
|
|
194
|
+
const finished = await finishedUpload(transport, upload, await file(change.localId));
|
|
195
|
+
if (finished?.body?.staged !== true)
|
|
384
196
|
return false;
|
|
385
197
|
}
|
|
386
198
|
return true;
|
|
387
199
|
}
|
|
388
|
-
async function submit(
|
|
200
|
+
async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
389
201
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
390
202
|
const prepared = uploadable(inline, bytes, declared())
|
|
391
203
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
392
204
|
: { body: inline };
|
|
393
205
|
if ('outcome' in prepared)
|
|
394
206
|
return prepared.outcome;
|
|
395
|
-
const answer = await transport.door(
|
|
396
|
-
const stagedFiles = await staged(answer,
|
|
207
|
+
const answer = await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
208
|
+
const stagedFiles = await staged(answer, request);
|
|
397
209
|
if (stagedFiles === false)
|
|
398
210
|
return UNCERTAIN;
|
|
399
|
-
const settled = stagedFiles === null ? answer : await transport.door(
|
|
400
|
-
if (stagedFiles === true && (await staged(settled,
|
|
211
|
+
const settled = stagedFiles === null ? answer : await transport.door(houseDoorRoute('batch'), prepared.body);
|
|
212
|
+
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
401
213
|
return UNCERTAIN;
|
|
402
214
|
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
403
215
|
? undefined
|
|
@@ -406,7 +218,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
406
218
|
destination: { authority: into.authority, handle: into.handle },
|
|
407
219
|
}));
|
|
408
220
|
}
|
|
409
|
-
function
|
|
221
|
+
function addressed(answer, locator) {
|
|
410
222
|
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
411
223
|
if (failure !== null) {
|
|
412
224
|
return failure.kind === 'scoped-refusal'
|
|
@@ -418,99 +230,71 @@ export function createAgentsHouseAdapter(options) {
|
|
|
418
230
|
}
|
|
419
231
|
: failure;
|
|
420
232
|
}
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
return
|
|
233
|
+
const address = parseFileAddress(answer.body);
|
|
234
|
+
return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
|
|
235
|
+
}
|
|
236
|
+
function linked(answer, locator) {
|
|
237
|
+
const address = addressed(answer, locator);
|
|
238
|
+
return address.kind === 'address'
|
|
239
|
+
? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
|
|
240
|
+
: address;
|
|
427
241
|
}
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
if (
|
|
431
|
-
return
|
|
432
|
-
|
|
433
|
-
|
|
242
|
+
function refusedAttach(answer, locator) {
|
|
243
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
244
|
+
if (failure === null)
|
|
245
|
+
return null;
|
|
246
|
+
return failure.kind === 'scoped-refusal'
|
|
247
|
+
? {
|
|
248
|
+
kind: 'rejected',
|
|
249
|
+
code: failure.code,
|
|
250
|
+
fields: failure.fields,
|
|
251
|
+
retryable: failure.retryable,
|
|
252
|
+
}
|
|
253
|
+
: failure;
|
|
254
|
+
}
|
|
255
|
+
async function downloaded(ask, locator) {
|
|
256
|
+
let address = addressed(await ask(), locator);
|
|
257
|
+
if (address.kind !== 'address')
|
|
258
|
+
return address;
|
|
259
|
+
let read = await transport.download(address.url);
|
|
260
|
+
if (read.status === EXPIRED_ADDRESS) {
|
|
261
|
+
address = addressed(await ask(), locator);
|
|
262
|
+
if (address.kind !== 'address')
|
|
263
|
+
return address;
|
|
264
|
+
read = await transport.download(address.url);
|
|
265
|
+
}
|
|
266
|
+
if (read.status !== 200 || read.body === null)
|
|
434
267
|
return UNCERTAIN_READ;
|
|
435
|
-
return {
|
|
268
|
+
return {
|
|
269
|
+
kind: 'binary',
|
|
270
|
+
body: read.body,
|
|
271
|
+
mediaType: address.mediaType,
|
|
272
|
+
sha256: address.sha256,
|
|
273
|
+
};
|
|
436
274
|
}
|
|
437
|
-
function
|
|
275
|
+
function pathAddress(request) {
|
|
438
276
|
const entry = authorityFor(request.locator);
|
|
439
277
|
if (entry === undefined)
|
|
440
278
|
return null;
|
|
441
|
-
return transport.door(
|
|
279
|
+
return () => transport.door(houseDoorRoute('files/get'), {
|
|
442
280
|
room_ref: entry.authority,
|
|
443
281
|
path: request.path,
|
|
444
282
|
revision: request.revision,
|
|
445
283
|
});
|
|
446
284
|
}
|
|
447
285
|
return {
|
|
286
|
+
roomTypes: HOUSE_ROOM_TYPES,
|
|
287
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
448
288
|
async authorize() {
|
|
449
289
|
return options.authorize();
|
|
450
290
|
},
|
|
451
|
-
async discover(_authorization, scope) {
|
|
452
|
-
const answer = await transport.door(DOOR_DISCOVER, {
|
|
453
|
-
prefixes: [...DISCOVER_PREFIXES],
|
|
454
|
-
...(scope === undefined ? {} : { rooms: scope.authorities }),
|
|
455
|
-
});
|
|
456
|
-
const failed = discoveryFailure(answer.status, answer.body);
|
|
457
|
-
if (failed !== null)
|
|
458
|
-
return failed;
|
|
459
|
-
const document = parseDiscovery(answer.body);
|
|
460
|
-
if (document === null)
|
|
461
|
-
return { kind: 'transport-uncertainty' };
|
|
462
|
-
bounds = document.bounds;
|
|
463
|
-
const entries = trackedFrom(document);
|
|
464
|
-
const covered = scope === undefined
|
|
465
|
-
? null
|
|
466
|
-
: [
|
|
467
|
-
...new Set([
|
|
468
|
-
...[...tracked.values()]
|
|
469
|
-
.filter((entry) => scope.authorities.includes(entry.authority))
|
|
470
|
-
.map((entry) => entry.locator),
|
|
471
|
-
...entries.map((entry) => entry.locator),
|
|
472
|
-
]),
|
|
473
|
-
];
|
|
474
|
-
if (covered === null)
|
|
475
|
-
tracked.clear();
|
|
476
|
-
else
|
|
477
|
-
for (const locator of covered)
|
|
478
|
-
tracked.delete(locator);
|
|
479
|
-
for (const entry of entries)
|
|
480
|
-
tracked.set(entry.locator, entry);
|
|
481
|
-
for (const locator of handed.keys())
|
|
482
|
-
if (!tracked.has(locator))
|
|
483
|
-
handed.delete(locator);
|
|
484
|
-
notifications.track([...tracked.values()]);
|
|
485
|
-
return {
|
|
486
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions, [...tracked.values()])),
|
|
487
|
-
member: document.member,
|
|
488
|
-
...(covered === null ? {} : { covered }),
|
|
489
|
-
};
|
|
490
|
-
},
|
|
491
291
|
async bootstrap(_authorization, request) {
|
|
492
292
|
const entry = authorityFor(request.locator);
|
|
493
293
|
if (entry === undefined)
|
|
494
294
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
return bootstrapTogether(asked);
|
|
500
|
-
},
|
|
501
|
-
async refresh(_authorization, request) {
|
|
502
|
-
const entry = authorityFor(request.locator);
|
|
503
|
-
if (entry === undefined)
|
|
504
|
-
return { kind: 'room-not-readable', locator: request.locator };
|
|
505
|
-
if (request.pageToken !== undefined)
|
|
506
|
-
return { kind: 'transport-uncertainty' };
|
|
507
|
-
const from = logPosition(request.fromRoomVersion);
|
|
508
|
-
if (from === null)
|
|
509
|
-
return { kind: 'beyond-horizon' };
|
|
510
|
-
const answered = await streamed(entry, request, from);
|
|
511
|
-
if (answered !== null)
|
|
512
|
-
return answered;
|
|
513
|
-
return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
|
|
295
|
+
return request.pageToken === undefined
|
|
296
|
+
? openedRoom(entry, request)
|
|
297
|
+
: streamedPage(entry, request.pageToken, request.held);
|
|
514
298
|
},
|
|
515
299
|
async mutate(_authorization, request, keep) {
|
|
516
300
|
const entry = authorityFor(request.locator);
|
|
@@ -531,18 +315,17 @@ export function createAgentsHouseAdapter(options) {
|
|
|
531
315
|
const delta = deltaBody(mapped.changes);
|
|
532
316
|
const bytes = deltaBytes(delta);
|
|
533
317
|
const handed = request.upload;
|
|
534
|
-
const answer = (upload) => submit(
|
|
535
|
-
const settle = (outcome) => into === undefined ? adopted(entry, outcome) : adoptedMove(entry, into, outcome);
|
|
318
|
+
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
536
319
|
const outcome = await answer(handed);
|
|
537
320
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
538
|
-
return
|
|
539
|
-
return
|
|
321
|
+
return outcome;
|
|
322
|
+
return answer(undefined);
|
|
540
323
|
},
|
|
541
324
|
async history(_authorization, request) {
|
|
542
325
|
const entry = authorityFor(request.locator);
|
|
543
326
|
if (entry === undefined)
|
|
544
327
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
545
|
-
const answer = await transport.door(
|
|
328
|
+
const answer = await transport.door(houseDoorRoute('revisions'), revisionBody({
|
|
546
329
|
path: request.path,
|
|
547
330
|
handle: entry.handle,
|
|
548
331
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
@@ -554,36 +337,45 @@ export function createAgentsHouseAdapter(options) {
|
|
|
554
337
|
});
|
|
555
338
|
},
|
|
556
339
|
async binary(_authorization, request) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
return downloaded(await asked, request.locator);
|
|
562
|
-
}
|
|
563
|
-
const referrer = houseReferrer(request.referrer, tracked);
|
|
564
|
-
if (referrer === null) {
|
|
565
|
-
return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
|
|
566
|
-
}
|
|
567
|
-
const answer = await transport.door(DOOR_FILES_GET, {
|
|
568
|
-
file: request.fileRef,
|
|
569
|
-
referrer: referrer.referrer,
|
|
570
|
-
});
|
|
571
|
-
return downloaded(answer, referrer.locator);
|
|
340
|
+
const ask = pathAddress(request);
|
|
341
|
+
if (ask === null)
|
|
342
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
343
|
+
return downloaded(ask, request.locator);
|
|
572
344
|
},
|
|
573
345
|
async link(_authorization, request) {
|
|
574
|
-
const
|
|
575
|
-
if (
|
|
346
|
+
const ask = pathAddress(request);
|
|
347
|
+
if (ask === null)
|
|
576
348
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
577
|
-
return
|
|
349
|
+
return linked(await ask(), request.locator);
|
|
578
350
|
},
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
if (
|
|
582
|
-
return
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
351
|
+
async attach(_authorization, request) {
|
|
352
|
+
const entry = authorityFor(request.locator);
|
|
353
|
+
if (entry === undefined)
|
|
354
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
355
|
+
const declared = await transport.door(houseDoorRoute('files/attach'), {
|
|
356
|
+
room_ref: entry.authority,
|
|
357
|
+
name: request.name,
|
|
358
|
+
bytes: request.byteLength,
|
|
359
|
+
sha256: request.sha256,
|
|
360
|
+
media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
|
|
361
|
+
});
|
|
362
|
+
const refused = refusedAttach(declared, request.locator);
|
|
363
|
+
if (refused !== null)
|
|
364
|
+
return refused;
|
|
365
|
+
const upload = houseUpload(declared.body?.upload);
|
|
366
|
+
if (upload === null)
|
|
367
|
+
return UNCERTAIN_READ;
|
|
368
|
+
const finished = await finishedUpload(transport, upload, request.blob);
|
|
369
|
+
if (finished === null)
|
|
370
|
+
return UNCERTAIN_READ;
|
|
371
|
+
const unfinished = refusedAttach(finished, request.locator);
|
|
372
|
+
if (unfinished !== null)
|
|
373
|
+
return unfinished;
|
|
374
|
+
const { path, revision } = (finished.body ?? {});
|
|
375
|
+
const root = `/rooms/${entry.handle}/`;
|
|
376
|
+
return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
|
|
377
|
+
? { kind: 'attached', path: path.slice(root.length), revision }
|
|
378
|
+
: UNCERTAIN_READ;
|
|
587
379
|
},
|
|
588
380
|
reached(locator, version, target) {
|
|
589
381
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
@@ -596,13 +388,7 @@ export function createAgentsHouseAdapter(options) {
|
|
|
596
388
|
},
|
|
597
389
|
roomStream: {
|
|
598
390
|
hold: held,
|
|
599
|
-
reopen: notifications.reopen,
|
|
600
391
|
observe: notifications.observe,
|
|
601
|
-
published(locator, roomVersion) {
|
|
602
|
-
const at = logPosition(roomVersion);
|
|
603
|
-
if (at !== null)
|
|
604
|
-
notifications.published(locator, at);
|
|
605
|
-
},
|
|
606
392
|
},
|
|
607
393
|
conversations: notifications.conversations,
|
|
608
394
|
serviceMode: notifications.serviceMode,
|