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