@agentshouse/mdruntime-adapter 0.1.0-cfd03209dcd82 → 0.1.0-cfd2dc1938a2c
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 -46
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +23 -342
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +250 -278
- 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/authored-path.d.ts +11 -0
- package/dist/authored-path.d.ts.map +1 -0
- package/dist/authored-path.js +35 -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 +23 -14
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +92 -54
- 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 +397 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/model.d.ts +5 -5
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +33 -43
- package/dist/read.d.ts +59 -44
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +158 -236
- package/dist/refusal.d.ts +1 -3
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +6 -6
- package/dist/room-types.d.ts +5 -0
- package/dist/room-types.d.ts.map +1 -0
- package/dist/room-types.js +120 -0
- 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 +1 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +110 -131
- 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 +15 -13
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +279 -159
- package/dist/transport.d.ts +81 -25
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +54 -11
- package/dist/upload.d.ts +12 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +63 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/write.d.ts +18 -2
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +108 -76
- package/package.json +8 -4
- package/AGENTS.md +0 -299
- package/dist/gather.d.ts +0 -4
- package/dist/gather.d.ts.map +0 -1
- package/dist/gather.js +0 -45
- package/dist/handed.d.ts +0 -15
- package/dist/handed.d.ts.map +0 -1
- package/dist/handed.js +0 -38
- 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,102 +1,124 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { gathered, gatheredEach, selections } from './gather.js';
|
|
1
|
+
import { stepped } from '@agentshouse/mdruntime';
|
|
2
|
+
import { capabilities, discoveredRoom, mapDiscovery, parseAuthorityChange, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
4
3
|
import { createSiteAdapter } from './site.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
4
|
+
import { parseFileAddress, parseGrant } from './read.js';
|
|
5
|
+
import { chunkedHead, chunkLister, chunkPart } from './chunks.js';
|
|
6
|
+
import { spaceSchemas } from './space-schema.js';
|
|
7
|
+
import { finishedUpload, houseUpload } from './upload.js';
|
|
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';
|
|
13
|
+
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, houseMove, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
14
|
+
import { admittedPath } from './authored-path.js';
|
|
15
|
+
import { HOUSE_TYPED_PATHS } from './house-model.js';
|
|
16
|
+
import { HOUSE_NAMEABLE_ROOM_TYPES, HOUSE_ROOM_TYPES } from './room-types.js';
|
|
17
|
+
import { followsVersion, reachedVersion, roomVersion } from './version.js';
|
|
14
18
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
15
19
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
20
|
+
const OCTET_STREAM = 'application/octet-stream';
|
|
21
|
+
const EXPIRED_ADDRESS = 403;
|
|
16
22
|
function lapsedGrant(outcome) {
|
|
17
23
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
18
24
|
}
|
|
19
25
|
export function createAgentsHouseAdapter(options) {
|
|
20
26
|
if ('site' in options)
|
|
21
|
-
return createSiteAdapter(options.site);
|
|
27
|
+
return createSiteAdapter(options.site, options.snapshot === true);
|
|
22
28
|
const transport = options.transport;
|
|
23
|
-
const
|
|
29
|
+
const spaces = spaceSchemas();
|
|
30
|
+
const notifications = houseNotifications(transport, 'credential', {
|
|
31
|
+
opened: async (frame) => (frame === null ? null : opened(frame)),
|
|
32
|
+
changed,
|
|
33
|
+
expired: (authorities) => lister.askAll(authorities.flatMap((authority) => [...tracked.values()].filter((entry) => entry.authority === authority))),
|
|
34
|
+
}, spaces);
|
|
24
35
|
const tracked = new Map();
|
|
25
|
-
const
|
|
36
|
+
const lister = chunkLister(transport, () => declared().max_prefixes);
|
|
37
|
+
const chunked = new Map();
|
|
38
|
+
const renewals = new WeakMap();
|
|
26
39
|
let bounds;
|
|
27
40
|
function declared() {
|
|
28
41
|
if (bounds === undefined)
|
|
29
42
|
throw new Error('no Discovery declared the bounds');
|
|
30
43
|
return bounds;
|
|
31
44
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
...
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
function opened(frame) {
|
|
46
|
+
const document = parseDiscovery(frame);
|
|
47
|
+
if (document === null)
|
|
48
|
+
return null;
|
|
49
|
+
bounds = document.bounds;
|
|
50
|
+
tracked.clear();
|
|
51
|
+
lister.forget();
|
|
52
|
+
for (const entry of trackedFrom(document))
|
|
53
|
+
tracked.set(entry.locator, entry);
|
|
54
|
+
notifications.track([...tracked.values()]);
|
|
55
|
+
return {
|
|
56
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions)),
|
|
57
|
+
member: document.member,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function changed(frame) {
|
|
61
|
+
const change = parseAuthorityChange(frame);
|
|
62
|
+
if (change === null)
|
|
63
|
+
return null;
|
|
64
|
+
for (const locator of change.left)
|
|
65
|
+
tracked.delete(locator);
|
|
66
|
+
for (const entry of trackedFrom(change))
|
|
67
|
+
tracked.set(entry.locator, entry);
|
|
68
|
+
notifications.track([...tracked.values()]);
|
|
47
69
|
return {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
70
|
+
kind: 'authority-changed',
|
|
71
|
+
rooms: change.authorities.map((authority) => discoveredRoom(authority)),
|
|
72
|
+
left: change.left,
|
|
73
|
+
member: change.member,
|
|
51
74
|
};
|
|
52
75
|
}
|
|
53
|
-
async function
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
handed.set(ask.entry.locator, enumeratedHanded(view, handed.get(ask.entry.locator)));
|
|
62
|
-
const models = await viewModels(view, ask.entry);
|
|
63
|
-
return models === undefined ? answer : { ...answer, models };
|
|
76
|
+
async function listedRoom(entry) {
|
|
77
|
+
const listed = await lister.list(entry);
|
|
78
|
+
if ('kind' in listed)
|
|
79
|
+
return listed;
|
|
80
|
+
const head = await chunkedHead(listed, entry, spaces);
|
|
81
|
+
if (head.kind === 'bootstrap')
|
|
82
|
+
chunked.set(entry.locator, listed);
|
|
83
|
+
return head;
|
|
64
84
|
}
|
|
65
|
-
async function
|
|
66
|
-
const
|
|
67
|
-
|
|
85
|
+
async function chunkRead(entry, id) {
|
|
86
|
+
const room = chunked.get(entry.locator);
|
|
87
|
+
const address = room?.chunks.get(id);
|
|
88
|
+
if (room === undefined || address === undefined)
|
|
68
89
|
return null;
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
90
|
+
const read = await transport.download(address.url);
|
|
91
|
+
if (read.status !== EXPIRED_ADDRESS)
|
|
92
|
+
return { room, read };
|
|
93
|
+
const renewed = await renewal(entry, room);
|
|
94
|
+
if ('kind' in renewed || !renewed.chunks.has(id))
|
|
71
95
|
return null;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
96
|
+
chunked.set(entry.locator, renewed);
|
|
97
|
+
return { room: renewed, read: await transport.download(renewed.chunks.get(id).url) };
|
|
75
98
|
}
|
|
76
|
-
function
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
function renewal(entry, stale) {
|
|
100
|
+
const held = renewals.get(stale);
|
|
101
|
+
if (held !== undefined)
|
|
102
|
+
return held;
|
|
103
|
+
const renewed = lister.renew(entry, [...chunked.keys()].flatMap((locator) => tracked.get(locator) ?? []));
|
|
104
|
+
renewals.set(stale, renewed);
|
|
105
|
+
return renewed;
|
|
106
|
+
}
|
|
107
|
+
function hold(rooms) {
|
|
108
|
+
for (const room of rooms) {
|
|
109
|
+
const listed = chunked.get(room.locator);
|
|
110
|
+
if (listed !== undefined &&
|
|
111
|
+
room.roomVersion !== null &&
|
|
112
|
+
reachedVersion(room.roomVersion, roomVersion(listed), listed.logEpoch))
|
|
113
|
+
chunked.delete(room.locator);
|
|
114
|
+
}
|
|
115
|
+
notifications.send(rooms);
|
|
87
116
|
}
|
|
88
|
-
function
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return outcome;
|
|
94
|
-
const follows = followsVersion(held.roomVersion, outcome.roomVersion);
|
|
95
|
-
handed.set(entry.locator, acceptedHanded(held, outcome, follows ? outcome.roomVersion : held.roomVersion));
|
|
96
|
-
const at = logPosition(outcome.roomVersion);
|
|
97
|
-
if (follows && at !== null)
|
|
98
|
-
notifications.reach(entry.authority, at);
|
|
99
|
-
return outcome;
|
|
117
|
+
async function chunkPage(entry, id, arriving) {
|
|
118
|
+
const fetched = await chunkRead(entry, id);
|
|
119
|
+
if (fetched === null || fetched.read.status !== 200)
|
|
120
|
+
return UNCERTAIN_READ;
|
|
121
|
+
return chunkPart(fetched.read.body, fetched.room, id, arriving);
|
|
100
122
|
}
|
|
101
123
|
function authorityFor(locator) {
|
|
102
124
|
return tracked.get(locator);
|
|
@@ -115,114 +137,25 @@ export function createAgentsHouseAdapter(options) {
|
|
|
115
137
|
return result;
|
|
116
138
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
117
139
|
}
|
|
118
|
-
function weight(entry) {
|
|
119
|
-
return contentPrefixes(entry).length;
|
|
120
|
-
}
|
|
121
|
-
async function bootstrapped(entries, answer) {
|
|
122
|
-
let open = entries.map((entry, ask) => ({ entry, ask }));
|
|
123
|
-
while (open.length > 0) {
|
|
124
|
-
const unreached = [];
|
|
125
|
-
for (const part of selections(open, ({ entry }) => weight(entry), declared().max_prefixes)) {
|
|
126
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
127
|
-
prefixes: part.flatMap(({ entry }) => contentPrefixes(entry)),
|
|
128
|
-
});
|
|
129
|
-
const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
|
|
130
|
-
const envelope = classified[0] === null ? parseEnvelope(answered.body) : null;
|
|
131
|
-
for (const [index, asked] of part.entries()) {
|
|
132
|
-
const failure = classified[index];
|
|
133
|
-
if (failure !== null && failure !== undefined) {
|
|
134
|
-
answer(asked.ask, failure);
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
if (envelope === null) {
|
|
138
|
-
answer(asked.ask, UNCERTAIN_READ);
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, asked.entry));
|
|
142
|
-
if (scope === undefined && envelope.next !== undefined) {
|
|
143
|
-
unreached.push(asked);
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.entry, null));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
open = unreached;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
async function continuedBootstrap(entry, continued) {
|
|
153
|
-
const after = { room: entry.authority, path: continued.after };
|
|
154
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
155
|
-
prefixes: contentPrefixes(entry),
|
|
156
|
-
after,
|
|
157
|
-
});
|
|
158
|
-
const failure = classifyDoor(answered.status, answered.body, entry.locator);
|
|
159
|
-
if (failure !== null)
|
|
160
|
-
return failure;
|
|
161
|
-
const envelope = parseEnvelope(answered.body);
|
|
162
|
-
if (envelope === null)
|
|
163
|
-
return UNCERTAIN_READ;
|
|
164
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, entry));
|
|
165
|
-
return bootstrappedRoom(envelope, scope, entry, continued);
|
|
166
|
-
}
|
|
167
|
-
function layered(asks) {
|
|
168
|
-
const depths = new Map();
|
|
169
|
-
const layers = [];
|
|
170
|
-
for (const ask of asks) {
|
|
171
|
-
const depth = depths.get(ask.entry.authority) ?? 0;
|
|
172
|
-
depths.set(ask.entry.authority, depth + 1);
|
|
173
|
-
(layers[depth] ??= []).push(ask);
|
|
174
|
-
}
|
|
175
|
-
return layers;
|
|
176
|
-
}
|
|
177
|
-
async function enumerated(asks) {
|
|
178
|
-
const answers = new Map();
|
|
179
|
-
const parts = layered(asks).flatMap((layer) => selections(layer, (ask) => weight(ask.entry), declared().max_prefixes));
|
|
180
|
-
for (const part of parts) {
|
|
181
|
-
let open = [...part];
|
|
182
|
-
while (open.length > 0) {
|
|
183
|
-
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
184
|
-
prefixes: open.flatMap((ask) => contentPrefixes(ask.entry)),
|
|
185
|
-
positions: open.map((ask) => presented(ask.entry.authority, ask.from)),
|
|
186
|
-
});
|
|
187
|
-
const classified = open.map((ask) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
188
|
-
if (classified[0]?.kind === 'beyond-horizon') {
|
|
189
|
-
const named = doorRefusal(answer.body)?.fields.authority;
|
|
190
|
-
const expired = open.filter((ask) => ask.entry.authority === named);
|
|
191
|
-
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
192
|
-
const gone = expired.length === 0 ? open : expired;
|
|
193
|
-
for (const ask of gone)
|
|
194
|
-
answers.set(ask, answered);
|
|
195
|
-
open = open.filter((ask) => !gone.includes(ask));
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
199
|
-
for (const [index, ask] of open.entries()) {
|
|
200
|
-
const failure = classified[index];
|
|
201
|
-
if (failure !== null && failure !== undefined) {
|
|
202
|
-
answers.set(ask, failure);
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
answers.set(ask, enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask));
|
|
206
|
-
}
|
|
207
|
-
open = [];
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return asks.map((ask) => answers.get(ask) ?? UNCERTAIN_READ);
|
|
211
|
-
}
|
|
212
|
-
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
213
|
-
const enumerateTogether = gathered(enumerated);
|
|
214
140
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
215
141
|
const paths = batchPaths(changes);
|
|
216
142
|
const sha256 = await deltaDigest(delta);
|
|
217
143
|
if (handed !== undefined && handed.sha256 === sha256) {
|
|
218
144
|
return { body: { ...head, paths, upload: handed, runtime: notifications.runtime } };
|
|
219
145
|
}
|
|
220
|
-
const minted = await transport.door(
|
|
146
|
+
const minted = await transport.door(houseDoorRoute('files/batch-upload'), {
|
|
147
|
+
...head,
|
|
148
|
+
paths,
|
|
149
|
+
bytes,
|
|
150
|
+
sha256,
|
|
151
|
+
});
|
|
221
152
|
if (minted.status !== 200) {
|
|
222
|
-
return {
|
|
153
|
+
return {
|
|
154
|
+
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
155
|
+
};
|
|
223
156
|
}
|
|
224
157
|
const grant = parseGrant(minted.body);
|
|
225
|
-
if (grant === null
|
|
158
|
+
if (grant === null)
|
|
226
159
|
return { outcome: UNCERTAIN };
|
|
227
160
|
const delivered = await transport.transfer({
|
|
228
161
|
act: 'authored_batch_upload',
|
|
@@ -232,52 +165,54 @@ export function createAgentsHouseAdapter(options) {
|
|
|
232
165
|
if (delivered.status !== 200)
|
|
233
166
|
return { outcome: UNCERTAIN };
|
|
234
167
|
const holding = parseGrant(delivered.body);
|
|
235
|
-
if (holding === null
|
|
168
|
+
if (holding === null)
|
|
236
169
|
return { outcome: UNCERTAIN };
|
|
237
170
|
const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
|
|
238
171
|
await keep(upload);
|
|
239
172
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
240
173
|
}
|
|
241
|
-
async function staged(answer, batch) {
|
|
174
|
+
async function staged(answer, { changeBatch: batch, file }) {
|
|
242
175
|
const refusal = doorRefusal(answer.body);
|
|
243
176
|
if (refusal?.code !== 'files_unstaged')
|
|
244
177
|
return null;
|
|
245
|
-
const
|
|
246
|
-
if (!Array.isArray(
|
|
178
|
+
const uploads = refusal.fields.upload;
|
|
179
|
+
if (!Array.isArray(uploads))
|
|
247
180
|
return false;
|
|
248
|
-
for (const
|
|
249
|
-
const
|
|
250
|
-
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === sha256);
|
|
251
|
-
if (
|
|
181
|
+
for (const named of uploads) {
|
|
182
|
+
const upload = houseUpload(named);
|
|
183
|
+
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
|
|
184
|
+
if (upload === null || change === undefined)
|
|
252
185
|
return false;
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
url,
|
|
256
|
-
body: change.blob,
|
|
257
|
-
operation,
|
|
258
|
-
});
|
|
259
|
-
if (delivered.status !== 200)
|
|
186
|
+
const finished = await finishedUpload(transport, upload, await file(change.localId));
|
|
187
|
+
if (finished?.body?.staged !== true)
|
|
260
188
|
return false;
|
|
261
189
|
}
|
|
262
190
|
return true;
|
|
263
191
|
}
|
|
264
|
-
async function submit(
|
|
192
|
+
async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
265
193
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
266
194
|
const prepared = uploadable(inline, bytes, declared())
|
|
267
195
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
268
196
|
: { body: inline };
|
|
269
197
|
if ('outcome' in prepared)
|
|
270
198
|
return prepared.outcome;
|
|
271
|
-
const answer = await transport.door(
|
|
272
|
-
const stagedFiles = await staged(answer,
|
|
199
|
+
const answer = await transport.door(houseDoorRoute('files/batch'), prepared.body);
|
|
200
|
+
const stagedFiles = await staged(answer, request);
|
|
273
201
|
if (stagedFiles === false)
|
|
274
202
|
return UNCERTAIN;
|
|
275
|
-
const settled = stagedFiles === null
|
|
276
|
-
|
|
203
|
+
const settled = stagedFiles === null
|
|
204
|
+
? answer
|
|
205
|
+
: await transport.door(houseDoorRoute('files/batch'), prepared.body);
|
|
206
|
+
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
277
207
|
return UNCERTAIN;
|
|
278
|
-
return mutationOutcome(settled.status, settled.body, entry.locator, entry.handle
|
|
208
|
+
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
209
|
+
? undefined
|
|
210
|
+
: {
|
|
211
|
+
source: { authority: entry.authority, handle: entry.handle },
|
|
212
|
+
destination: { authority: into.authority, handle: into.handle },
|
|
213
|
+
}));
|
|
279
214
|
}
|
|
280
|
-
|
|
215
|
+
function addressed(answer, locator) {
|
|
281
216
|
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
282
217
|
if (failure !== null) {
|
|
283
218
|
return failure.kind === 'scoped-refusal'
|
|
@@ -289,85 +224,106 @@ export function createAgentsHouseAdapter(options) {
|
|
|
289
224
|
}
|
|
290
225
|
: failure;
|
|
291
226
|
}
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
227
|
+
const address = parseFileAddress(answer.body);
|
|
228
|
+
return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
|
|
229
|
+
}
|
|
230
|
+
function linked(answer, locator) {
|
|
231
|
+
const address = addressed(answer, locator);
|
|
232
|
+
return address.kind === 'address'
|
|
233
|
+
? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
|
|
234
|
+
: address;
|
|
235
|
+
}
|
|
236
|
+
function refusedAttach(answer, locator) {
|
|
237
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
238
|
+
if (failure === null)
|
|
239
|
+
return null;
|
|
240
|
+
return failure.kind === 'scoped-refusal'
|
|
241
|
+
? {
|
|
242
|
+
kind: 'rejected',
|
|
243
|
+
code: failure.code,
|
|
244
|
+
fields: failure.fields,
|
|
245
|
+
retryable: failure.retryable,
|
|
246
|
+
}
|
|
247
|
+
: failure;
|
|
248
|
+
}
|
|
249
|
+
async function downloaded(ask, locator) {
|
|
250
|
+
let address = addressed(await ask(), locator);
|
|
251
|
+
if (address.kind !== 'address')
|
|
252
|
+
return address;
|
|
253
|
+
let read = await transport.download(address.url);
|
|
254
|
+
if (read.status === EXPIRED_ADDRESS) {
|
|
255
|
+
address = addressed(await ask(), locator);
|
|
256
|
+
if (address.kind !== 'address')
|
|
257
|
+
return address;
|
|
258
|
+
read = await transport.download(address.url);
|
|
259
|
+
}
|
|
260
|
+
if (read.status !== 200 || read.body === null)
|
|
297
261
|
return UNCERTAIN_READ;
|
|
298
|
-
return {
|
|
262
|
+
return {
|
|
263
|
+
kind: 'binary',
|
|
264
|
+
body: read.body,
|
|
265
|
+
mediaType: address.mediaType,
|
|
266
|
+
sha256: address.sha256,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function pathAddress(request) {
|
|
270
|
+
const entry = authorityFor(request.locator);
|
|
271
|
+
if (entry === undefined)
|
|
272
|
+
return null;
|
|
273
|
+
return () => transport.door(houseDoorRoute('files/read'), {
|
|
274
|
+
room_ref: entry.authority,
|
|
275
|
+
path: request.path,
|
|
276
|
+
revision: request.revision,
|
|
277
|
+
});
|
|
299
278
|
}
|
|
300
279
|
return {
|
|
280
|
+
roomTypes: HOUSE_ROOM_TYPES,
|
|
281
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
282
|
+
typedPaths: HOUSE_TYPED_PATHS,
|
|
283
|
+
pathRule: admittedPath,
|
|
284
|
+
unkeptReads: HOUSE_UNKEPT_READS,
|
|
285
|
+
leadingReads: HOUSE_LEADING_READS,
|
|
301
286
|
async authorize() {
|
|
302
287
|
return options.authorize();
|
|
303
288
|
},
|
|
304
|
-
async discover(_authorization) {
|
|
305
|
-
const answer = await transport.door(DOOR_DISCOVER, { prefixes: [...DISCOVER_PREFIXES] });
|
|
306
|
-
const failed = discoveryFailure(answer.status, answer.body);
|
|
307
|
-
if (failed !== null)
|
|
308
|
-
return failed;
|
|
309
|
-
const document = parseDiscovery(answer.body);
|
|
310
|
-
if (document === null)
|
|
311
|
-
return { kind: 'transport-uncertainty' };
|
|
312
|
-
bounds = document.bounds;
|
|
313
|
-
const entries = trackedFrom(document);
|
|
314
|
-
tracked.clear();
|
|
315
|
-
for (const entry of entries)
|
|
316
|
-
tracked.set(entry.locator, entry);
|
|
317
|
-
for (const locator of handed.keys())
|
|
318
|
-
if (!tracked.has(locator))
|
|
319
|
-
handed.delete(locator);
|
|
320
|
-
notifications.track(entries);
|
|
321
|
-
return {
|
|
322
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document)),
|
|
323
|
-
member: document.member,
|
|
324
|
-
};
|
|
325
|
-
},
|
|
326
289
|
async bootstrap(_authorization, request) {
|
|
327
290
|
const entry = authorityFor(request.locator);
|
|
328
291
|
if (entry === undefined)
|
|
329
292
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
return bootstrapTogether(entry);
|
|
334
|
-
},
|
|
335
|
-
async refresh(_authorization, request) {
|
|
336
|
-
const entry = authorityFor(request.locator);
|
|
337
|
-
if (entry === undefined)
|
|
338
|
-
return { kind: 'room-not-readable', locator: request.locator };
|
|
339
|
-
if (request.pageToken !== undefined)
|
|
340
|
-
return { kind: 'transport-uncertainty' };
|
|
341
|
-
const from = logPosition(request.fromRoomVersion);
|
|
342
|
-
if (from === null)
|
|
343
|
-
return { kind: 'beyond-horizon' };
|
|
344
|
-
const answered = await streamed(entry, request, from);
|
|
345
|
-
if (answered !== null)
|
|
346
|
-
return answered;
|
|
347
|
-
return enumerateTogether({ entry, from, fromRoomVersion: request.fromRoomVersion });
|
|
293
|
+
return request.pageToken === undefined
|
|
294
|
+
? listedRoom(entry)
|
|
295
|
+
: chunkPage(entry, request.pageToken, request.arriving);
|
|
348
296
|
},
|
|
349
297
|
async mutate(_authorization, request, keep) {
|
|
350
298
|
const entry = authorityFor(request.locator);
|
|
351
299
|
if (entry === undefined)
|
|
352
300
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
353
|
-
const
|
|
301
|
+
const batch = request.changeBatch;
|
|
302
|
+
const destination = batch.between?.destination.room;
|
|
303
|
+
const into = destination === undefined ? undefined : authorityFor(destination);
|
|
304
|
+
if (destination !== undefined && into === undefined) {
|
|
305
|
+
return { kind: 'room-not-readable', locator: destination };
|
|
306
|
+
}
|
|
307
|
+
const mapped = into === undefined
|
|
308
|
+
? houseChanges(batch, entry.handle)
|
|
309
|
+
: houseMove(batch, entry.handle, into.handle);
|
|
354
310
|
if (mapped.kind === 'unmapped')
|
|
355
311
|
return rejectedMapping(mapped);
|
|
356
|
-
const head = operationHead(
|
|
312
|
+
const head = operationHead(batch, entry.authority);
|
|
357
313
|
const delta = deltaBody(mapped.changes);
|
|
358
314
|
const bytes = deltaBytes(delta);
|
|
359
315
|
const handed = request.upload;
|
|
360
|
-
const
|
|
361
|
-
const outcome = await
|
|
316
|
+
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
317
|
+
const outcome = await answer(handed);
|
|
362
318
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
363
|
-
return
|
|
364
|
-
return
|
|
319
|
+
return outcome;
|
|
320
|
+
return answer(undefined);
|
|
365
321
|
},
|
|
366
322
|
async history(_authorization, request) {
|
|
367
323
|
const entry = authorityFor(request.locator);
|
|
368
324
|
if (entry === undefined)
|
|
369
325
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
370
|
-
const answer = await transport.door(
|
|
326
|
+
const answer = await transport.door(houseDoorRoute('files/revisions/list'), revisionBody({
|
|
371
327
|
path: request.path,
|
|
372
328
|
handle: entry.handle,
|
|
373
329
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
@@ -379,35 +335,45 @@ export function createAgentsHouseAdapter(options) {
|
|
|
379
335
|
});
|
|
380
336
|
},
|
|
381
337
|
async binary(_authorization, request) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const answer = await transport.door(DOOR_FILES_GET, {
|
|
387
|
-
room_ref: entry.authority,
|
|
388
|
-
path: request.path,
|
|
389
|
-
revision: request.revision,
|
|
390
|
-
});
|
|
391
|
-
return downloaded(answer, request.locator);
|
|
392
|
-
}
|
|
393
|
-
const referrer = houseReferrer(request.referrer, tracked);
|
|
394
|
-
if (referrer === null) {
|
|
395
|
-
return { kind: 'rejected', code: 'referrer_unknown', fields: {}, retryable: false };
|
|
396
|
-
}
|
|
397
|
-
const answer = await transport.door(DOOR_FILES_GET, {
|
|
398
|
-
file: request.fileRef,
|
|
399
|
-
referrer: referrer.referrer,
|
|
400
|
-
});
|
|
401
|
-
return downloaded(answer, referrer.locator);
|
|
338
|
+
const ask = pathAddress(request);
|
|
339
|
+
if (ask === null)
|
|
340
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
341
|
+
return downloaded(ask, request.locator);
|
|
402
342
|
},
|
|
403
|
-
|
|
404
|
-
const
|
|
405
|
-
if (
|
|
406
|
-
return
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
343
|
+
async link(_authorization, request) {
|
|
344
|
+
const ask = pathAddress(request);
|
|
345
|
+
if (ask === null)
|
|
346
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
347
|
+
return linked(await ask(), request.locator);
|
|
348
|
+
},
|
|
349
|
+
async attach(_authorization, request) {
|
|
350
|
+
const entry = authorityFor(request.locator);
|
|
351
|
+
if (entry === undefined)
|
|
352
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
353
|
+
const declared = await transport.door(houseDoorRoute('files/attach'), {
|
|
354
|
+
room_ref: entry.authority,
|
|
355
|
+
name: request.name,
|
|
356
|
+
bytes: request.byteLength,
|
|
357
|
+
sha256: request.sha256,
|
|
358
|
+
media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
|
|
359
|
+
});
|
|
360
|
+
const refused = refusedAttach(declared, request.locator);
|
|
361
|
+
if (refused !== null)
|
|
362
|
+
return refused;
|
|
363
|
+
const upload = houseUpload(declared.body?.upload);
|
|
364
|
+
if (upload === null)
|
|
365
|
+
return UNCERTAIN_READ;
|
|
366
|
+
const finished = await finishedUpload(transport, upload, request.blob);
|
|
367
|
+
if (finished === null)
|
|
368
|
+
return UNCERTAIN_READ;
|
|
369
|
+
const unfinished = refusedAttach(finished, request.locator);
|
|
370
|
+
if (unfinished !== null)
|
|
371
|
+
return unfinished;
|
|
372
|
+
const { path, revision } = (finished.body ?? {});
|
|
373
|
+
const root = `/rooms/${entry.handle}/`;
|
|
374
|
+
return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
|
|
375
|
+
? { kind: 'attached', path: path.slice(root.length), revision }
|
|
376
|
+
: UNCERTAIN_READ;
|
|
411
377
|
},
|
|
412
378
|
reached(locator, version, target) {
|
|
413
379
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
@@ -418,7 +384,13 @@ export function createAgentsHouseAdapter(options) {
|
|
|
418
384
|
async action(_authorization, request) {
|
|
419
385
|
return withAt(await houseAction(transport, request));
|
|
420
386
|
},
|
|
421
|
-
|
|
387
|
+
search(_authorization, request) {
|
|
388
|
+
return houseSearch(transport, [...tracked.values()], request);
|
|
389
|
+
},
|
|
390
|
+
roomStream: {
|
|
391
|
+
hold,
|
|
392
|
+
observe: notifications.observe,
|
|
393
|
+
},
|
|
422
394
|
conversations: notifications.conversations,
|
|
423
395
|
serviceMode: notifications.serviceMode,
|
|
424
396
|
};
|