@agentshouse/mdruntime-adapter 0.1.0-cd61dfca5d699 → 0.1.0-cd673d02b6e50
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 -23
- 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 +204 -201
- 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 +36 -32
- package/dist/gather.d.ts +1 -2
- package/dist/gather.d.ts.map +1 -1
- package/dist/gather.js +5 -15
- package/dist/house-model.d.ts +2 -0
- package/dist/house-model.d.ts.map +1 -0
- package/dist/house-model.js +486 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -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 +14 -0
- package/dist/part.d.ts.map +1 -0
- package/dist/part.js +90 -0
- package/dist/read.d.ts +53 -27
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +262 -158
- 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 +125 -0
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +47 -112
- 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 +14 -8
- 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 +16 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +100 -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 {
|
|
3
|
-
import { gathered,
|
|
3
|
+
import { streamedModels, viewModels } from './model.js';
|
|
4
|
+
import { gathered, selections } from './gather.js';
|
|
4
5
|
import { createSiteAdapter } from './site.js';
|
|
5
|
-
import {
|
|
6
|
+
import { authorityRoot, bootstrapping, parseEnumeration, parseFileAddress, parseGrant, refreshFrom, viewOf, wholeView, } from './read.js';
|
|
7
|
+
import { roomStreams } from './room-stream.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,52 @@ export function createAgentsHouseAdapter(options) {
|
|
|
51
54
|
const notifications = houseNotifications(transport, 'credential');
|
|
52
55
|
const tracked = new Map();
|
|
53
56
|
const handed = new Map();
|
|
57
|
+
const rooms = roomStreams('credential');
|
|
58
|
+
const unreused = new Set();
|
|
59
|
+
let heldRevisions;
|
|
54
60
|
let bounds;
|
|
55
61
|
function declared() {
|
|
56
62
|
if (bounds === undefined)
|
|
57
63
|
throw new Error('no Discovery declared the bounds');
|
|
58
64
|
return bounds;
|
|
59
65
|
}
|
|
60
|
-
async function
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
63
|
-
return
|
|
64
|
-
const scope = await reused(
|
|
66
|
+
async function streamedPage(entry, token, held) {
|
|
67
|
+
const part = await rooms.part(entry, token);
|
|
68
|
+
if ('kind' in part)
|
|
69
|
+
return part;
|
|
70
|
+
const scope = await reused(part.scope, held);
|
|
65
71
|
if (scope === null) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
: continued === null
|
|
69
|
-
? bootstrapTogether({ entry, held: undefined })
|
|
70
|
-
: continuedBootstrap({ entry, held: undefined }, continued);
|
|
72
|
+
unreused.add(entry.locator);
|
|
73
|
+
return UNCERTAIN_READ;
|
|
71
74
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (continued === null && next === null)
|
|
75
|
-
handed.set(entry.locator, scopeHanded(scope));
|
|
75
|
+
if (part.first && part.ended)
|
|
76
|
+
handed.set(entry.locator, roomVersion(scope));
|
|
76
77
|
else
|
|
77
78
|
handed.delete(entry.locator);
|
|
78
79
|
notifications.reach(entry.authority, scope);
|
|
79
|
-
const seen = [
|
|
80
|
-
...(continued?.seen ?? []),
|
|
81
|
-
...entry.schemas.filter((schema) => scope.files.some((file) => file.path === schema)),
|
|
82
|
-
];
|
|
83
|
-
const models = await partModels(scope, entry, new Set(seen), next === null);
|
|
84
|
-
const continuation = next === null
|
|
85
|
-
? null
|
|
86
|
-
: { after: next, seen, ...(cursor?.cut === undefined ? {} : { cut: cursor.cut }) };
|
|
87
80
|
return {
|
|
88
|
-
...
|
|
89
|
-
...(models === undefined ? {} : { models }),
|
|
90
|
-
...(
|
|
81
|
+
...(await stepped(bootstrapping(scope))),
|
|
82
|
+
...(part.models === undefined ? {} : { models: part.models }),
|
|
83
|
+
...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
|
|
84
|
+
bytes: part.bytes,
|
|
91
85
|
};
|
|
92
86
|
}
|
|
87
|
+
async function openedRoom(entry, request) {
|
|
88
|
+
const reusing = unreused.delete(entry.locator) ? undefined : request.held;
|
|
89
|
+
const files = reusing === undefined
|
|
90
|
+
? null
|
|
91
|
+
: await heldFiles(entry.authority, reusing, declared().max_held_files);
|
|
92
|
+
const answered = await transport.streamed(DOOR_BOOTSTRAP, {
|
|
93
|
+
prefixes: [authorityRoot(entry.handle)],
|
|
94
|
+
...(files === null ? {} : { held: [files] }),
|
|
95
|
+
}, request.signal ?? new AbortController().signal);
|
|
96
|
+
if (answered.room === null)
|
|
97
|
+
return classifyDoor(answered.status, answered.body, entry.locator) ?? UNCERTAIN_READ;
|
|
98
|
+
return rooms.head(entry, answered.room, request.arriving, request.signal);
|
|
99
|
+
}
|
|
93
100
|
async function enumeratedPage(ask, page) {
|
|
94
101
|
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
95
|
-
prefixes:
|
|
102
|
+
prefixes: [authorityRoot(ask.entry.handle)],
|
|
96
103
|
positions: [
|
|
97
104
|
{
|
|
98
105
|
...presented(ask.entry.authority, { position: page.after, logEpoch: ask.from.logEpoch }),
|
|
@@ -116,21 +123,24 @@ export function createAgentsHouseAdapter(options) {
|
|
|
116
123
|
if ('kind' in view)
|
|
117
124
|
return view;
|
|
118
125
|
notifications.reach(ask.entry.authority, view);
|
|
119
|
-
const answer = refreshFrom(view, ask.fromRoomVersion);
|
|
126
|
+
const answer = await refreshFrom(view, ask.fromRoomVersion);
|
|
120
127
|
if (answer.kind !== 'net-advancement')
|
|
121
128
|
return answer;
|
|
122
|
-
handed.set(ask.entry.locator,
|
|
129
|
+
handed.set(ask.entry.locator, roomVersion(view));
|
|
123
130
|
const models = await viewModels(view, ask.entry);
|
|
124
131
|
return models === undefined ? answer : { ...answer, models };
|
|
125
132
|
}
|
|
126
133
|
async function streamed(entry, request, from) {
|
|
127
|
-
|
|
128
|
-
if (held?.roomVersion !== request.fromRoomVersion)
|
|
134
|
+
if (handed.get(entry.locator) !== request.fromRoomVersion)
|
|
129
135
|
return null;
|
|
130
|
-
const
|
|
136
|
+
const asked = notifications.edited(entry, from);
|
|
137
|
+
const kept = asked.length === 0 || heldRevisions === undefined
|
|
138
|
+
? new Map()
|
|
139
|
+
: await heldRevisions(entry.locator, asked);
|
|
140
|
+
const advanced = await stepped(notifications.advance(entry, from, (path, revision) => kept.get(path) === revision));
|
|
131
141
|
if (advanced === null)
|
|
132
142
|
return null;
|
|
133
|
-
handed.set(entry.locator,
|
|
143
|
+
handed.set(entry.locator, advanced.page.roomVersion);
|
|
134
144
|
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
135
145
|
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
136
146
|
}
|
|
@@ -140,20 +150,27 @@ export function createAgentsHouseAdapter(options) {
|
|
|
140
150
|
const at = logPosition(room.roomVersion);
|
|
141
151
|
if (entry === undefined || at === null)
|
|
142
152
|
return [];
|
|
143
|
-
handed.set(room.locator,
|
|
153
|
+
handed.set(room.locator, room.roomVersion);
|
|
144
154
|
notifications.reach(entry.authority, at);
|
|
145
155
|
return [presented(entry.authority, at)];
|
|
146
156
|
});
|
|
147
157
|
notifications.send(kept);
|
|
148
158
|
}
|
|
149
|
-
function
|
|
159
|
+
async function adoptedMove(entry, into, outcome) {
|
|
160
|
+
if (outcome.kind === 'accepted' && outcome.destination !== undefined) {
|
|
161
|
+
await adopted(into, { kind: 'accepted', ...outcome.destination });
|
|
162
|
+
}
|
|
163
|
+
return adopted(entry, outcome);
|
|
164
|
+
}
|
|
165
|
+
async function adopted(entry, outcome) {
|
|
150
166
|
if (outcome.kind !== 'accepted')
|
|
151
167
|
return outcome;
|
|
152
168
|
const held = handed.get(entry.locator);
|
|
153
169
|
if (held === undefined)
|
|
154
170
|
return outcome;
|
|
155
|
-
const follows = followsVersion(held
|
|
156
|
-
|
|
171
|
+
const follows = followsVersion(held, outcome.roomVersion);
|
|
172
|
+
if (follows)
|
|
173
|
+
handed.set(entry.locator, outcome.roomVersion);
|
|
157
174
|
const at = logPosition(outcome.roomVersion);
|
|
158
175
|
if (follows && at !== null)
|
|
159
176
|
notifications.reach(entry.authority, at);
|
|
@@ -176,68 +193,6 @@ export function createAgentsHouseAdapter(options) {
|
|
|
176
193
|
return result;
|
|
177
194
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
178
195
|
}
|
|
179
|
-
function weight(entry) {
|
|
180
|
-
return contentPrefixes(entry).length;
|
|
181
|
-
}
|
|
182
|
-
function holding(asks, after) {
|
|
183
|
-
const budget = Math.floor(declared().max_held_files / Math.max(1, asks.filter((ask) => ask.held !== undefined).length));
|
|
184
|
-
return asks.flatMap((ask) => heldFiles(ask.entry.authority, ask.held, after, budget) ?? []);
|
|
185
|
-
}
|
|
186
|
-
async function bootstrapped(asks, answer) {
|
|
187
|
-
let open = asks.map((asked, ask) => ({ entry: asked.entry, asked, ask }));
|
|
188
|
-
while (open.length > 0) {
|
|
189
|
-
const unreached = [];
|
|
190
|
-
for (const part of selections(open, ({ entry }) => weight(entry), declared().max_prefixes)) {
|
|
191
|
-
const held = holding(part.map(({ asked }) => asked), null);
|
|
192
|
-
const answered = await transport.door(DOOR_BOOTSTRAP, {
|
|
193
|
-
prefixes: part.flatMap(({ entry }) => contentPrefixes(entry)),
|
|
194
|
-
...(held.length === 0 ? {} : { held }),
|
|
195
|
-
});
|
|
196
|
-
const classified = part.map(({ entry }) => classifyDoor(answered.status, answered.body, entry.locator));
|
|
197
|
-
const envelope = classified[0] === null ? parseEnvelope(answered.body) : null;
|
|
198
|
-
for (const [index, asked] of part.entries()) {
|
|
199
|
-
const failure = classified[index];
|
|
200
|
-
if (failure !== null && failure !== undefined) {
|
|
201
|
-
answer(asked.ask, failure);
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
if (envelope === null) {
|
|
205
|
-
answer(asked.ask, UNCERTAIN_READ);
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
const scope = envelope.scopes.find((candidate) => matchesScope(candidate, asked.entry));
|
|
209
|
-
if (scope === undefined && envelope.next !== undefined) {
|
|
210
|
-
unreached.push(asked);
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
answer(asked.ask, await bootstrappedRoom(envelope, scope, asked.asked, null));
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
open = unreached;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
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
196
|
function layered(asks) {
|
|
242
197
|
const depths = new Map();
|
|
243
198
|
const layers = [];
|
|
@@ -250,12 +205,12 @@ export function createAgentsHouseAdapter(options) {
|
|
|
250
205
|
}
|
|
251
206
|
async function enumerated(asks) {
|
|
252
207
|
const answers = [];
|
|
253
|
-
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer,
|
|
208
|
+
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, declared().max_prefixes));
|
|
254
209
|
for (const part of parts) {
|
|
255
210
|
let open = [...part];
|
|
256
211
|
while (open.length > 0) {
|
|
257
212
|
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
258
|
-
prefixes: open.
|
|
213
|
+
prefixes: open.map(({ ask }) => authorityRoot(ask.entry.handle)),
|
|
259
214
|
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
260
215
|
});
|
|
261
216
|
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
@@ -284,7 +239,6 @@ export function createAgentsHouseAdapter(options) {
|
|
|
284
239
|
}
|
|
285
240
|
return answers;
|
|
286
241
|
}
|
|
287
|
-
const bootstrapTogether = gatheredEach(bootstrapped);
|
|
288
242
|
const enumerateTogether = gathered(enumerated);
|
|
289
243
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
290
244
|
const paths = batchPaths(changes);
|
|
@@ -294,10 +248,12 @@ export function createAgentsHouseAdapter(options) {
|
|
|
294
248
|
}
|
|
295
249
|
const minted = await transport.door(DOOR_BATCH_UPLOAD, { ...head, paths, bytes, sha256 });
|
|
296
250
|
if (minted.status !== 200) {
|
|
297
|
-
return {
|
|
251
|
+
return {
|
|
252
|
+
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
253
|
+
};
|
|
298
254
|
}
|
|
299
255
|
const grant = parseGrant(minted.body);
|
|
300
|
-
if (grant === null
|
|
256
|
+
if (grant === null)
|
|
301
257
|
return { outcome: UNCERTAIN };
|
|
302
258
|
const delivered = await transport.transfer({
|
|
303
259
|
act: 'authored_batch_upload',
|
|
@@ -307,36 +263,31 @@ export function createAgentsHouseAdapter(options) {
|
|
|
307
263
|
if (delivered.status !== 200)
|
|
308
264
|
return { outcome: UNCERTAIN };
|
|
309
265
|
const holding = parseGrant(delivered.body);
|
|
310
|
-
if (holding === null
|
|
266
|
+
if (holding === null)
|
|
311
267
|
return { outcome: UNCERTAIN };
|
|
312
268
|
const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
|
|
313
269
|
await keep(upload);
|
|
314
270
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
315
271
|
}
|
|
316
|
-
async function staged(answer, batch) {
|
|
272
|
+
async function staged(answer, { changeBatch: batch, file }) {
|
|
317
273
|
const refusal = doorRefusal(answer.body);
|
|
318
274
|
if (refusal?.code !== 'files_unstaged')
|
|
319
275
|
return null;
|
|
320
|
-
const
|
|
321
|
-
if (!Array.isArray(
|
|
276
|
+
const uploads = refusal.fields.upload;
|
|
277
|
+
if (!Array.isArray(uploads))
|
|
322
278
|
return false;
|
|
323
|
-
for (const
|
|
324
|
-
const
|
|
325
|
-
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === sha256);
|
|
326
|
-
if (
|
|
279
|
+
for (const named of uploads) {
|
|
280
|
+
const upload = houseUpload(named);
|
|
281
|
+
const change = batch.changes.find((held) => held.kind === 'binary' && held.sha256 === named.sha256);
|
|
282
|
+
if (upload === null || change === undefined)
|
|
327
283
|
return false;
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
url,
|
|
331
|
-
body: change.blob,
|
|
332
|
-
operation,
|
|
333
|
-
});
|
|
334
|
-
if (delivered.status !== 200)
|
|
284
|
+
const finished = await finishedUpload(transport, upload, await file(change.localId));
|
|
285
|
+
if (finished?.body?.staged !== true)
|
|
335
286
|
return false;
|
|
336
287
|
}
|
|
337
288
|
return true;
|
|
338
289
|
}
|
|
339
|
-
async function submit(
|
|
290
|
+
async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
340
291
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
341
292
|
const prepared = uploadable(inline, bytes, declared())
|
|
342
293
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
@@ -344,15 +295,20 @@ export function createAgentsHouseAdapter(options) {
|
|
|
344
295
|
if ('outcome' in prepared)
|
|
345
296
|
return prepared.outcome;
|
|
346
297
|
const answer = await transport.door(DOOR_BATCH, prepared.body);
|
|
347
|
-
const stagedFiles = await staged(answer,
|
|
298
|
+
const stagedFiles = await staged(answer, request);
|
|
348
299
|
if (stagedFiles === false)
|
|
349
300
|
return UNCERTAIN;
|
|
350
301
|
const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
|
|
351
|
-
if (stagedFiles === true && (await staged(settled,
|
|
302
|
+
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
352
303
|
return UNCERTAIN;
|
|
353
|
-
return mutationOutcome(settled.status, settled.body, entry.locator, entry.handle
|
|
304
|
+
return stepped(mutationOutcome(settled.status, settled.body, entry.locator, entry.handle, into === undefined
|
|
305
|
+
? undefined
|
|
306
|
+
: {
|
|
307
|
+
source: { authority: entry.authority, handle: entry.handle },
|
|
308
|
+
destination: { authority: into.authority, handle: into.handle },
|
|
309
|
+
}));
|
|
354
310
|
}
|
|
355
|
-
function
|
|
311
|
+
function addressed(answer, locator) {
|
|
356
312
|
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
357
313
|
if (failure !== null) {
|
|
358
314
|
return failure.kind === 'scoped-refusal'
|
|
@@ -364,33 +320,61 @@ export function createAgentsHouseAdapter(options) {
|
|
|
364
320
|
}
|
|
365
321
|
: failure;
|
|
366
322
|
}
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
if (grant === null || grant.method !== 'GET' || typeof name !== 'string') {
|
|
370
|
-
return UNCERTAIN_READ;
|
|
371
|
-
}
|
|
372
|
-
return { kind: 'link', url: grant.url, name };
|
|
323
|
+
const address = parseFileAddress(answer.body);
|
|
324
|
+
return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
|
|
373
325
|
}
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
326
|
+
function linked(answer, locator) {
|
|
327
|
+
const address = addressed(answer, locator);
|
|
328
|
+
return address.kind === 'address'
|
|
329
|
+
? { kind: 'link', url: address.url, name: address.name, mediaType: address.mediaType }
|
|
330
|
+
: address;
|
|
331
|
+
}
|
|
332
|
+
function refusedAttach(answer, locator) {
|
|
333
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
334
|
+
if (failure === null)
|
|
335
|
+
return null;
|
|
336
|
+
return failure.kind === 'scoped-refusal'
|
|
337
|
+
? {
|
|
338
|
+
kind: 'rejected',
|
|
339
|
+
code: failure.code,
|
|
340
|
+
fields: failure.fields,
|
|
341
|
+
retryable: failure.retryable,
|
|
342
|
+
}
|
|
343
|
+
: failure;
|
|
344
|
+
}
|
|
345
|
+
async function downloaded(ask, locator) {
|
|
346
|
+
let address = addressed(await ask(), locator);
|
|
347
|
+
if (address.kind !== 'address')
|
|
348
|
+
return address;
|
|
349
|
+
let read = await transport.download(address.url);
|
|
350
|
+
if (read.status === EXPIRED_ADDRESS) {
|
|
351
|
+
address = addressed(await ask(), locator);
|
|
352
|
+
if (address.kind !== 'address')
|
|
353
|
+
return address;
|
|
354
|
+
read = await transport.download(address.url);
|
|
355
|
+
}
|
|
356
|
+
if (read.status !== 200 || read.body === null)
|
|
380
357
|
return UNCERTAIN_READ;
|
|
381
|
-
return {
|
|
358
|
+
return {
|
|
359
|
+
kind: 'binary',
|
|
360
|
+
body: read.body,
|
|
361
|
+
mediaType: address.mediaType,
|
|
362
|
+
sha256: address.sha256,
|
|
363
|
+
};
|
|
382
364
|
}
|
|
383
|
-
function
|
|
365
|
+
function pathAddress(request) {
|
|
384
366
|
const entry = authorityFor(request.locator);
|
|
385
367
|
if (entry === undefined)
|
|
386
368
|
return null;
|
|
387
|
-
return transport.door(DOOR_FILES_GET, {
|
|
369
|
+
return () => transport.door(DOOR_FILES_GET, {
|
|
388
370
|
room_ref: entry.authority,
|
|
389
371
|
path: request.path,
|
|
390
372
|
revision: request.revision,
|
|
391
373
|
});
|
|
392
374
|
}
|
|
393
375
|
return {
|
|
376
|
+
roomTypes: HOUSE_ROOM_TYPES,
|
|
377
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
394
378
|
async authorize() {
|
|
395
379
|
return options.authorize();
|
|
396
380
|
},
|
|
@@ -438,11 +422,9 @@ export function createAgentsHouseAdapter(options) {
|
|
|
438
422
|
const entry = authorityFor(request.locator);
|
|
439
423
|
if (entry === undefined)
|
|
440
424
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
445
|
-
return bootstrapTogether(asked);
|
|
425
|
+
return request.pageToken === undefined
|
|
426
|
+
? openedRoom(entry, request)
|
|
427
|
+
: streamedPage(entry, request.pageToken, request.held);
|
|
446
428
|
},
|
|
447
429
|
async refresh(_authorization, request) {
|
|
448
430
|
const entry = authorityFor(request.locator);
|
|
@@ -462,18 +444,27 @@ export function createAgentsHouseAdapter(options) {
|
|
|
462
444
|
const entry = authorityFor(request.locator);
|
|
463
445
|
if (entry === undefined)
|
|
464
446
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
465
|
-
const
|
|
447
|
+
const batch = request.changeBatch;
|
|
448
|
+
const destination = batch.between?.destination.room;
|
|
449
|
+
const into = destination === undefined ? undefined : authorityFor(destination);
|
|
450
|
+
if (destination !== undefined && into === undefined) {
|
|
451
|
+
return { kind: 'room-not-readable', locator: destination };
|
|
452
|
+
}
|
|
453
|
+
const mapped = into === undefined
|
|
454
|
+
? houseChanges(batch, entry.handle)
|
|
455
|
+
: houseMove(batch, entry.handle, into.handle);
|
|
466
456
|
if (mapped.kind === 'unmapped')
|
|
467
457
|
return rejectedMapping(mapped);
|
|
468
|
-
const head = operationHead(
|
|
458
|
+
const head = operationHead(batch);
|
|
469
459
|
const delta = deltaBody(mapped.changes);
|
|
470
460
|
const bytes = deltaBytes(delta);
|
|
471
461
|
const handed = request.upload;
|
|
472
|
-
const
|
|
473
|
-
const
|
|
462
|
+
const answer = (upload) => submit(request, head, mapped.changes, delta, bytes, entry, upload, keep, into);
|
|
463
|
+
const settle = (outcome) => into === undefined ? adopted(entry, outcome) : adoptedMove(entry, into, outcome);
|
|
464
|
+
const outcome = await answer(handed);
|
|
474
465
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
475
|
-
return
|
|
476
|
-
return
|
|
466
|
+
return settle(outcome);
|
|
467
|
+
return settle(await answer(undefined));
|
|
477
468
|
},
|
|
478
469
|
async history(_authorization, request) {
|
|
479
470
|
const entry = authorityFor(request.locator);
|
|
@@ -491,36 +482,45 @@ export function createAgentsHouseAdapter(options) {
|
|
|
491
482
|
});
|
|
492
483
|
},
|
|
493
484
|
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);
|
|
485
|
+
const ask = pathAddress(request);
|
|
486
|
+
if (ask === null)
|
|
487
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
488
|
+
return downloaded(ask, request.locator);
|
|
509
489
|
},
|
|
510
490
|
async link(_authorization, request) {
|
|
511
|
-
const
|
|
512
|
-
if (
|
|
491
|
+
const ask = pathAddress(request);
|
|
492
|
+
if (ask === null)
|
|
513
493
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
514
|
-
return
|
|
494
|
+
return linked(await ask(), request.locator);
|
|
515
495
|
},
|
|
516
|
-
|
|
517
|
-
const
|
|
518
|
-
if (
|
|
519
|
-
return
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
496
|
+
async attach(_authorization, request) {
|
|
497
|
+
const entry = authorityFor(request.locator);
|
|
498
|
+
if (entry === undefined)
|
|
499
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
500
|
+
const declared = await transport.door(DOOR_FILES_ATTACH, {
|
|
501
|
+
room_ref: entry.authority,
|
|
502
|
+
name: request.name,
|
|
503
|
+
bytes: request.byteLength,
|
|
504
|
+
sha256: request.sha256,
|
|
505
|
+
media_type: request.mediaType.length > 0 ? request.mediaType : OCTET_STREAM,
|
|
506
|
+
});
|
|
507
|
+
const refused = refusedAttach(declared, request.locator);
|
|
508
|
+
if (refused !== null)
|
|
509
|
+
return refused;
|
|
510
|
+
const upload = houseUpload(declared.body?.upload);
|
|
511
|
+
if (upload === null)
|
|
512
|
+
return UNCERTAIN_READ;
|
|
513
|
+
const finished = await finishedUpload(transport, upload, request.blob);
|
|
514
|
+
if (finished === null)
|
|
515
|
+
return UNCERTAIN_READ;
|
|
516
|
+
const unfinished = refusedAttach(finished, request.locator);
|
|
517
|
+
if (unfinished !== null)
|
|
518
|
+
return unfinished;
|
|
519
|
+
const { path, revision } = (finished.body ?? {});
|
|
520
|
+
const root = `/rooms/${entry.handle}/`;
|
|
521
|
+
return typeof path === 'string' && path.startsWith(root) && typeof revision === 'string'
|
|
522
|
+
? { kind: 'attached', path: path.slice(root.length), revision }
|
|
523
|
+
: UNCERTAIN_READ;
|
|
524
524
|
},
|
|
525
525
|
reached(locator, version, target) {
|
|
526
526
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
@@ -534,7 +534,10 @@ export function createAgentsHouseAdapter(options) {
|
|
|
534
534
|
roomStream: {
|
|
535
535
|
hold: held,
|
|
536
536
|
reopen: notifications.reopen,
|
|
537
|
-
observe
|
|
537
|
+
observe(notify, receipts, revisions) {
|
|
538
|
+
heldRevisions = revisions;
|
|
539
|
+
return notifications.observe(notify, receipts);
|
|
540
|
+
},
|
|
538
541
|
published(locator, roomVersion) {
|
|
539
542
|
const at = logPosition(roomVersion);
|
|
540
543
|
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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crockford.d.ts","sourceRoot":"","sources":["../src/crockford.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AAErE,eAAO,MAAM,mBAAmB,uCAA4B,CAAC;AAE7D,eAAO,MAAM,2BAA2B,QAAqC,CAAC"}
|