@agentshouse/mdruntime-adapter 0.1.0-cea44707d3919 → 0.1.0-ceda07a375a48
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 +163 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +123 -214
- package/dist/adapter.d.ts +1 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +333 -109
- 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 +7 -10
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +44 -36
- package/dist/gather.d.ts +1 -1
- package/dist/gather.d.ts.map +1 -1
- package/dist/gather.js +15 -20
- package/dist/house-model.d.ts +2935 -0
- package/dist/house-model.d.ts.map +1 -0
- package/dist/house-model.js +1613 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/model.d.ts +6 -2
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +17 -15
- 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 +73 -20
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +349 -166
- package/dist/refusal.d.ts +2 -5
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +6 -9
- package/dist/room-stream.d.ts +19 -0
- package/dist/room-stream.d.ts.map +1 -0
- package/dist/room-stream.js +57 -0
- package/dist/room-types.d.ts +7 -0
- package/dist/room-types.d.ts.map +1 -0
- package/dist/room-types.js +126 -0
- package/dist/site.d.ts +1 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +131 -63
- package/dist/stream.d.ts +5 -4
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +361 -44
- package/dist/transport.d.ts +75 -11
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +4 -0
- 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 -9
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +164 -41
- package/package.json +9 -6
- package/AGENTS.md +0 -253
- package/dist/links.d.ts +0 -20
- package/dist/links.d.ts.map +0 -1
- package/dist/links.js +0 -104
package/dist/adapter.js
CHANGED
|
@@ -1,65 +1,146 @@
|
|
|
1
|
+
import { stepped } from '@agentshouse/mdruntime';
|
|
1
2
|
import { DISCOVER_PREFIXES, capabilities, discoveredRoom, mapDiscovery, parseDiscovery, trackedFrom, } from './discovery.js';
|
|
2
|
-
import {
|
|
3
|
+
import { streamedModels, viewModels } from './model.js';
|
|
3
4
|
import { gathered, selections } from './gather.js';
|
|
4
5
|
import { createSiteAdapter } from './site.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { authorityRoot, bootstrapping, parseEnumeration, parseFileAddress, parseGrant, refreshFrom, viewOf, wholeView, } from './read.js';
|
|
7
|
+
import { roomStreams } from './room-stream.js';
|
|
8
|
+
import { sha256Hex } from './digest.js';
|
|
9
|
+
import { finishedUpload, houseUpload } from './upload.js';
|
|
7
10
|
import { houseAction } from './actions.js';
|
|
8
11
|
import { classifyDoor, classifyRead, discoveryFailure, doorRefusal } from './refusal.js';
|
|
9
12
|
import { houseNotifications } from './stream.js';
|
|
10
|
-
import { DOOR_BATCH, DOOR_BATCH_UPLOAD, DOOR_BOOTSTRAP, DOOR_DISCOVER, DOOR_ENUMERATE, DOOR_REVISIONS, } from './transport.js';
|
|
11
|
-
import { batchPaths, deltaBody, deltaBytes, deltaDigest, historyOutcome, houseChanges, mutationOutcome, operationHead, rejectedMapping, revisionBody, uploadable, } from './write.js';
|
|
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';
|
|
12
16
|
import { followsVersion, logPosition, presented, reachedVersion, roomVersion, } from './version.js';
|
|
13
17
|
const UNCERTAIN = { kind: 'transport-uncertainty' };
|
|
14
18
|
const UNCERTAIN_READ = { kind: 'transport-uncertainty' };
|
|
19
|
+
const OCTET_STREAM = 'application/octet-stream';
|
|
20
|
+
const EXPIRED_ADDRESS = 403;
|
|
15
21
|
function lapsedGrant(outcome) {
|
|
16
22
|
return outcome.kind === 'rejected' && outcome.code === 'upload_unavailable';
|
|
17
23
|
}
|
|
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 };
|
|
29
|
+
}
|
|
30
|
+
async function reused(scope, held) {
|
|
31
|
+
const sent = new Set(scope.contents.map((entry) => entry.path));
|
|
32
|
+
const contents = [...scope.contents];
|
|
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) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
contents.push({ path: file.path, content: found.content });
|
|
47
|
+
}
|
|
48
|
+
return { ...scope, contents };
|
|
49
|
+
}
|
|
18
50
|
export function createAgentsHouseAdapter(options) {
|
|
19
51
|
if ('site' in options)
|
|
20
|
-
return createSiteAdapter(options.site);
|
|
52
|
+
return createSiteAdapter(options.site, options.snapshot === true);
|
|
21
53
|
const transport = options.transport;
|
|
22
54
|
const notifications = houseNotifications(transport, 'credential');
|
|
23
55
|
const tracked = new Map();
|
|
24
56
|
const handed = new Map();
|
|
57
|
+
const rooms = roomStreams('credential');
|
|
58
|
+
const unreused = new Set();
|
|
59
|
+
let heldRevisions;
|
|
25
60
|
let bounds;
|
|
26
61
|
function declared() {
|
|
27
62
|
if (bounds === undefined)
|
|
28
63
|
throw new Error('no Discovery declared the bounds');
|
|
29
64
|
return bounds;
|
|
30
65
|
}
|
|
31
|
-
async function
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
return
|
|
35
|
-
|
|
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);
|
|
71
|
+
if (scope === null) {
|
|
72
|
+
unreused.add(entry.locator);
|
|
73
|
+
return UNCERTAIN_READ;
|
|
74
|
+
}
|
|
75
|
+
if (part.first && part.ended)
|
|
76
|
+
handed.set(entry.locator, roomVersion(scope));
|
|
77
|
+
else
|
|
78
|
+
handed.delete(entry.locator);
|
|
36
79
|
notifications.reach(entry.authority, scope);
|
|
37
|
-
|
|
38
|
-
|
|
80
|
+
return {
|
|
81
|
+
...(await stepped(bootstrapping(scope))),
|
|
82
|
+
...(part.models === undefined ? {} : { models: part.models }),
|
|
83
|
+
...(part.pageToken === undefined ? {} : { pageToken: part.pageToken }),
|
|
84
|
+
bytes: part.bytes,
|
|
85
|
+
};
|
|
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
|
+
}
|
|
100
|
+
async function enumeratedPage(ask, page) {
|
|
101
|
+
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
102
|
+
prefixes: [authorityRoot(ask.entry.handle)],
|
|
103
|
+
positions: [
|
|
104
|
+
{
|
|
105
|
+
...presented(ask.entry.authority, { position: page.after, logEpoch: ask.from.logEpoch }),
|
|
106
|
+
page: { through: page.through, read: page.read },
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
const failure = classifyRead(answer.status, answer.body, ask.entry.locator);
|
|
111
|
+
if (failure !== null)
|
|
112
|
+
return failure;
|
|
113
|
+
const enumeration = parseEnumeration(answer.body, 'credential');
|
|
114
|
+
if (enumeration === null)
|
|
115
|
+
return UNCERTAIN_READ;
|
|
116
|
+
return (viewOf(enumeration, ask.entry) ?? { kind: 'room-not-readable', locator: ask.entry.locator });
|
|
39
117
|
}
|
|
40
118
|
async function enumeratedRoom(enumeration, ask) {
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
119
|
+
const first = viewOf(enumeration, ask.entry);
|
|
120
|
+
if (first === undefined)
|
|
43
121
|
return { kind: 'room-not-readable', locator: ask.entry.locator };
|
|
122
|
+
const view = await wholeView(first, (page) => enumeratedPage(ask, page));
|
|
123
|
+
if ('kind' in view)
|
|
124
|
+
return view;
|
|
44
125
|
notifications.reach(ask.entry.authority, view);
|
|
45
|
-
const answer = refreshFrom(view, ask.fromRoomVersion);
|
|
126
|
+
const answer = await refreshFrom(view, ask.fromRoomVersion);
|
|
46
127
|
if (answer.kind !== 'net-advancement')
|
|
47
128
|
return answer;
|
|
48
|
-
handed.set(ask.entry.locator,
|
|
129
|
+
handed.set(ask.entry.locator, roomVersion(view));
|
|
49
130
|
const models = await viewModels(view, ask.entry);
|
|
50
131
|
return models === undefined ? answer : { ...answer, models };
|
|
51
132
|
}
|
|
52
133
|
async function streamed(entry, request, from) {
|
|
53
|
-
|
|
54
|
-
if (links?.roomVersion !== request.fromRoomVersion)
|
|
134
|
+
if (handed.get(entry.locator) !== request.fromRoomVersion)
|
|
55
135
|
return null;
|
|
56
|
-
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));
|
|
57
141
|
if (advanced === null)
|
|
58
142
|
return null;
|
|
59
|
-
|
|
60
|
-
if (relinked === null)
|
|
61
|
-
return null;
|
|
62
|
-
handed.set(entry.locator, relinked);
|
|
143
|
+
handed.set(entry.locator, advanced.page.roomVersion);
|
|
63
144
|
const models = await streamedModels(advanced.schemas, entry, 'credential');
|
|
64
145
|
return models === undefined ? advanced.page : { ...advanced.page, models };
|
|
65
146
|
}
|
|
@@ -69,28 +150,31 @@ export function createAgentsHouseAdapter(options) {
|
|
|
69
150
|
const at = logPosition(room.roomVersion);
|
|
70
151
|
if (entry === undefined || at === null)
|
|
71
152
|
return [];
|
|
72
|
-
handed.set(room.locator,
|
|
153
|
+
handed.set(room.locator, room.roomVersion);
|
|
73
154
|
notifications.reach(entry.authority, at);
|
|
74
155
|
return [presented(entry.authority, at)];
|
|
75
156
|
});
|
|
76
157
|
notifications.send(kept);
|
|
77
158
|
}
|
|
78
|
-
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) {
|
|
79
166
|
if (outcome.kind !== 'accepted')
|
|
80
167
|
return outcome;
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const at = logPosition(accepted.roomVersion);
|
|
168
|
+
const held = handed.get(entry.locator);
|
|
169
|
+
if (held === undefined)
|
|
170
|
+
return outcome;
|
|
171
|
+
const follows = followsVersion(held, outcome.roomVersion);
|
|
172
|
+
if (follows)
|
|
173
|
+
handed.set(entry.locator, outcome.roomVersion);
|
|
174
|
+
const at = logPosition(outcome.roomVersion);
|
|
89
175
|
if (follows && at !== null)
|
|
90
176
|
notifications.reach(entry.authority, at);
|
|
91
|
-
return
|
|
92
|
-
? accepted
|
|
93
|
-
: { ...accepted, attachments: relinked.attachments };
|
|
177
|
+
return outcome;
|
|
94
178
|
}
|
|
95
179
|
function authorityFor(locator) {
|
|
96
180
|
return tracked.get(locator);
|
|
@@ -109,88 +193,52 @@ export function createAgentsHouseAdapter(options) {
|
|
|
109
193
|
return result;
|
|
110
194
|
return { ...result, at: { locator, roomVersion: roomVersion({ position, logEpoch }) } };
|
|
111
195
|
}
|
|
112
|
-
async function bundled(body) {
|
|
113
|
-
const grant = parseGrant(body);
|
|
114
|
-
if (grant === null)
|
|
115
|
-
return body;
|
|
116
|
-
if (grant.method !== 'GET')
|
|
117
|
-
return null;
|
|
118
|
-
const answer = await transport.transfer({
|
|
119
|
-
act: 'local_copy_bundle',
|
|
120
|
-
method: 'GET',
|
|
121
|
-
url: grant.url,
|
|
122
|
-
answer: 'json',
|
|
123
|
-
});
|
|
124
|
-
return answer.status === 200 ? answer.body : null;
|
|
125
|
-
}
|
|
126
|
-
function weight(entry) {
|
|
127
|
-
return contentPrefixes(entry).length;
|
|
128
|
-
}
|
|
129
|
-
async function bootstrapped(entries) {
|
|
130
|
-
const answers = [];
|
|
131
|
-
for (const part of selections(entries, weight, declared().max_prefixes)) {
|
|
132
|
-
const answer = await transport.door(DOOR_BOOTSTRAP, {
|
|
133
|
-
prefixes: part.flatMap((entry) => contentPrefixes(entry)),
|
|
134
|
-
});
|
|
135
|
-
const classified = part.map((entry) => classifyDoor(answer.status, answer.body, entry.locator));
|
|
136
|
-
const envelope = classified[0] === null ? parseEnvelope(await bundled(answer.body)) : null;
|
|
137
|
-
for (const [index, entry] of part.entries()) {
|
|
138
|
-
const failure = classified[index];
|
|
139
|
-
if (failure === null || failure === undefined) {
|
|
140
|
-
answers.push(envelope === null ? UNCERTAIN_READ : await bootstrappedRoom(envelope, entry));
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
answers.push(failure);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return answers;
|
|
147
|
-
}
|
|
148
196
|
function layered(asks) {
|
|
149
197
|
const depths = new Map();
|
|
150
198
|
const layers = [];
|
|
151
199
|
for (const ask of asks) {
|
|
152
|
-
const depth = depths.get(ask.entry.authority) ?? 0;
|
|
153
|
-
depths.set(ask.entry.authority, depth + 1);
|
|
200
|
+
const depth = depths.get(ask.ask.entry.authority) ?? 0;
|
|
201
|
+
depths.set(ask.ask.entry.authority, depth + 1);
|
|
154
202
|
(layers[depth] ??= []).push(ask);
|
|
155
203
|
}
|
|
156
204
|
return layers;
|
|
157
205
|
}
|
|
158
206
|
async function enumerated(asks) {
|
|
159
|
-
const answers =
|
|
160
|
-
const parts = layered(asks
|
|
207
|
+
const answers = [];
|
|
208
|
+
const parts = layered(asks.map((ask, index) => ({ ask, index }))).flatMap((layer) => selections(layer, declared().max_prefixes));
|
|
161
209
|
for (const part of parts) {
|
|
162
210
|
let open = [...part];
|
|
163
211
|
while (open.length > 0) {
|
|
164
212
|
const answer = await transport.door(DOOR_ENUMERATE, {
|
|
165
|
-
prefixes: open.
|
|
166
|
-
positions: open.map((ask) => presented(ask.entry.authority, ask.from)),
|
|
213
|
+
prefixes: open.map(({ ask }) => authorityRoot(ask.entry.handle)),
|
|
214
|
+
positions: open.map(({ ask }) => presented(ask.entry.authority, ask.from)),
|
|
167
215
|
});
|
|
168
|
-
const classified = open.map((ask) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
216
|
+
const classified = open.map(({ ask }) => classifyRead(answer.status, answer.body, ask.entry.locator));
|
|
169
217
|
if (classified[0]?.kind === 'beyond-horizon') {
|
|
170
218
|
const named = doorRefusal(answer.body)?.fields.authority;
|
|
171
|
-
const expired = open.filter((ask) => ask.entry.authority === named);
|
|
219
|
+
const expired = open.filter(({ ask }) => ask.entry.authority === named);
|
|
172
220
|
const answered = expired.length === 0 ? UNCERTAIN_READ : { kind: 'beyond-horizon' };
|
|
173
221
|
const gone = expired.length === 0 ? open : expired;
|
|
174
222
|
for (const ask of gone)
|
|
175
|
-
answers.
|
|
223
|
+
answers[ask.index] = answered;
|
|
176
224
|
open = open.filter((ask) => !gone.includes(ask));
|
|
177
225
|
continue;
|
|
178
226
|
}
|
|
179
227
|
const enumeration = classified[0] === null ? parseEnumeration(answer.body, 'credential') : null;
|
|
180
|
-
for (const [
|
|
181
|
-
const failure = classified[
|
|
228
|
+
for (const [answerIndex, { ask, index }] of open.entries()) {
|
|
229
|
+
const failure = classified[answerIndex];
|
|
182
230
|
if (failure !== null && failure !== undefined) {
|
|
183
|
-
answers
|
|
231
|
+
answers[index] = failure;
|
|
184
232
|
continue;
|
|
185
233
|
}
|
|
186
|
-
answers
|
|
234
|
+
answers[index] =
|
|
235
|
+
enumeration === null ? UNCERTAIN_READ : await enumeratedRoom(enumeration, ask);
|
|
187
236
|
}
|
|
188
237
|
open = [];
|
|
189
238
|
}
|
|
190
239
|
}
|
|
191
|
-
return
|
|
240
|
+
return answers;
|
|
192
241
|
}
|
|
193
|
-
const bootstrapTogether = gathered(bootstrapped);
|
|
194
242
|
const enumerateTogether = gathered(enumerated);
|
|
195
243
|
async function uploaded(head, changes, delta, bytes, entry, handed, keep) {
|
|
196
244
|
const paths = batchPaths(changes);
|
|
@@ -200,28 +248,46 @@ export function createAgentsHouseAdapter(options) {
|
|
|
200
248
|
}
|
|
201
249
|
const minted = await transport.door(DOOR_BATCH_UPLOAD, { ...head, paths, bytes, sha256 });
|
|
202
250
|
if (minted.status !== 200) {
|
|
203
|
-
return {
|
|
251
|
+
return {
|
|
252
|
+
outcome: await stepped(mutationOutcome(minted.status, minted.body, entry.locator, entry.handle)),
|
|
253
|
+
};
|
|
204
254
|
}
|
|
205
255
|
const grant = parseGrant(minted.body);
|
|
206
|
-
if (grant === null
|
|
256
|
+
if (grant === null)
|
|
207
257
|
return { outcome: UNCERTAIN };
|
|
208
258
|
const delivered = await transport.transfer({
|
|
209
259
|
act: 'authored_batch_upload',
|
|
210
|
-
method: 'POST',
|
|
211
260
|
url: grant.url,
|
|
212
|
-
answer: 'json',
|
|
213
261
|
body: `${grant.operation}\n${delta}`,
|
|
214
262
|
});
|
|
215
263
|
if (delivered.status !== 200)
|
|
216
264
|
return { outcome: UNCERTAIN };
|
|
217
265
|
const holding = parseGrant(delivered.body);
|
|
218
|
-
if (holding === null
|
|
266
|
+
if (holding === null)
|
|
219
267
|
return { outcome: UNCERTAIN };
|
|
220
268
|
const upload = { url: holding.url, operation: holding.operation, bytes, sha256 };
|
|
221
269
|
await keep(upload);
|
|
222
270
|
return { body: { ...head, paths, upload, runtime: notifications.runtime } };
|
|
223
271
|
}
|
|
224
|
-
async function
|
|
272
|
+
async function staged(answer, { changeBatch: batch, file }) {
|
|
273
|
+
const refusal = doorRefusal(answer.body);
|
|
274
|
+
if (refusal?.code !== 'files_unstaged')
|
|
275
|
+
return null;
|
|
276
|
+
const uploads = refusal.fields.upload;
|
|
277
|
+
if (!Array.isArray(uploads))
|
|
278
|
+
return false;
|
|
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)
|
|
283
|
+
return false;
|
|
284
|
+
const finished = await finishedUpload(transport, upload, await file(change.localId));
|
|
285
|
+
if (finished?.body?.staged !== true)
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
async function submit(request, head, changes, delta, bytes, entry, handed, keep, into) {
|
|
225
291
|
const inline = { ...head, changes, runtime: notifications.runtime };
|
|
226
292
|
const prepared = uploadable(inline, bytes, declared())
|
|
227
293
|
? await uploaded(head, changes, delta, bytes, entry, handed, keep)
|
|
@@ -229,14 +295,94 @@ export function createAgentsHouseAdapter(options) {
|
|
|
229
295
|
if ('outcome' in prepared)
|
|
230
296
|
return prepared.outcome;
|
|
231
297
|
const answer = await transport.door(DOOR_BATCH, prepared.body);
|
|
232
|
-
|
|
298
|
+
const stagedFiles = await staged(answer, request);
|
|
299
|
+
if (stagedFiles === false)
|
|
300
|
+
return UNCERTAIN;
|
|
301
|
+
const settled = stagedFiles === null ? answer : await transport.door(DOOR_BATCH, prepared.body);
|
|
302
|
+
if (stagedFiles === true && (await staged(settled, request)) !== null)
|
|
303
|
+
return UNCERTAIN;
|
|
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
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
function addressed(answer, locator) {
|
|
312
|
+
const failure = classifyDoor(answer.status, answer.body, locator);
|
|
313
|
+
if (failure !== null) {
|
|
314
|
+
return failure.kind === 'scoped-refusal'
|
|
315
|
+
? {
|
|
316
|
+
kind: 'rejected',
|
|
317
|
+
code: failure.code,
|
|
318
|
+
fields: failure.fields,
|
|
319
|
+
retryable: failure.retryable,
|
|
320
|
+
}
|
|
321
|
+
: failure;
|
|
322
|
+
}
|
|
323
|
+
const address = parseFileAddress(answer.body);
|
|
324
|
+
return address === null ? UNCERTAIN_READ : { kind: 'address', ...address };
|
|
325
|
+
}
|
|
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)
|
|
357
|
+
return UNCERTAIN_READ;
|
|
358
|
+
return {
|
|
359
|
+
kind: 'binary',
|
|
360
|
+
body: read.body,
|
|
361
|
+
mediaType: address.mediaType,
|
|
362
|
+
sha256: address.sha256,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function pathAddress(request) {
|
|
366
|
+
const entry = authorityFor(request.locator);
|
|
367
|
+
if (entry === undefined)
|
|
368
|
+
return null;
|
|
369
|
+
return () => transport.door(DOOR_FILES_GET, {
|
|
370
|
+
room_ref: entry.authority,
|
|
371
|
+
path: request.path,
|
|
372
|
+
revision: request.revision,
|
|
373
|
+
});
|
|
233
374
|
}
|
|
234
375
|
return {
|
|
376
|
+
roomTypes: HOUSE_ROOM_TYPES,
|
|
377
|
+
nameableRoomTypes: HOUSE_NAMEABLE_ROOM_TYPES,
|
|
235
378
|
async authorize() {
|
|
236
379
|
return options.authorize();
|
|
237
380
|
},
|
|
238
|
-
async discover(_authorization) {
|
|
239
|
-
const answer = await transport.door(DOOR_DISCOVER, {
|
|
381
|
+
async discover(_authorization, scope) {
|
|
382
|
+
const answer = await transport.door(DOOR_DISCOVER, {
|
|
383
|
+
prefixes: [...DISCOVER_PREFIXES],
|
|
384
|
+
...(scope === undefined ? {} : { rooms: scope.authorities }),
|
|
385
|
+
});
|
|
240
386
|
const failed = discoveryFailure(answer.status, answer.body);
|
|
241
387
|
if (failed !== null)
|
|
242
388
|
return failed;
|
|
@@ -245,25 +391,40 @@ export function createAgentsHouseAdapter(options) {
|
|
|
245
391
|
return { kind: 'transport-uncertainty' };
|
|
246
392
|
bounds = document.bounds;
|
|
247
393
|
const entries = trackedFrom(document);
|
|
248
|
-
|
|
394
|
+
const covered = scope === undefined
|
|
395
|
+
? null
|
|
396
|
+
: [
|
|
397
|
+
...new Set([
|
|
398
|
+
...[...tracked.values()]
|
|
399
|
+
.filter((entry) => scope.authorities.includes(entry.authority))
|
|
400
|
+
.map((entry) => entry.locator),
|
|
401
|
+
...entries.map((entry) => entry.locator),
|
|
402
|
+
]),
|
|
403
|
+
];
|
|
404
|
+
if (covered === null)
|
|
405
|
+
tracked.clear();
|
|
406
|
+
else
|
|
407
|
+
for (const locator of covered)
|
|
408
|
+
tracked.delete(locator);
|
|
249
409
|
for (const entry of entries)
|
|
250
410
|
tracked.set(entry.locator, entry);
|
|
251
411
|
for (const locator of handed.keys())
|
|
252
412
|
if (!tracked.has(locator))
|
|
253
413
|
handed.delete(locator);
|
|
254
|
-
notifications.track(
|
|
414
|
+
notifications.track([...tracked.values()]);
|
|
255
415
|
return {
|
|
256
|
-
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document)),
|
|
416
|
+
...mapDiscovery(document, document.authorities.map((authority) => discoveredRoom(authority)), capabilities(document.actions, [...tracked.values()])),
|
|
257
417
|
member: document.member,
|
|
418
|
+
...(covered === null ? {} : { covered }),
|
|
258
419
|
};
|
|
259
420
|
},
|
|
260
421
|
async bootstrap(_authorization, request) {
|
|
261
422
|
const entry = authorityFor(request.locator);
|
|
262
423
|
if (entry === undefined)
|
|
263
424
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
425
|
+
return request.pageToken === undefined
|
|
426
|
+
? openedRoom(entry, request)
|
|
427
|
+
: streamedPage(entry, request.pageToken, request.held);
|
|
267
428
|
},
|
|
268
429
|
async refresh(_authorization, request) {
|
|
269
430
|
const entry = authorityFor(request.locator);
|
|
@@ -283,17 +444,27 @@ export function createAgentsHouseAdapter(options) {
|
|
|
283
444
|
const entry = authorityFor(request.locator);
|
|
284
445
|
if (entry === undefined)
|
|
285
446
|
return { kind: 'room-not-readable', locator: request.locator };
|
|
286
|
-
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);
|
|
287
456
|
if (mapped.kind === 'unmapped')
|
|
288
457
|
return rejectedMapping(mapped);
|
|
289
|
-
const head = operationHead(
|
|
458
|
+
const head = operationHead(batch);
|
|
290
459
|
const delta = deltaBody(mapped.changes);
|
|
291
460
|
const bytes = deltaBytes(delta);
|
|
292
461
|
const handed = request.upload;
|
|
293
|
-
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);
|
|
294
465
|
if (handed === undefined || !lapsedGrant(outcome))
|
|
295
|
-
return
|
|
296
|
-
return
|
|
466
|
+
return settle(outcome);
|
|
467
|
+
return settle(await answer(undefined));
|
|
297
468
|
},
|
|
298
469
|
async history(_authorization, request) {
|
|
299
470
|
const entry = authorityFor(request.locator);
|
|
@@ -310,6 +481,47 @@ export function createAgentsHouseAdapter(options) {
|
|
|
310
481
|
...(request.revision === undefined ? {} : { revision: request.revision }),
|
|
311
482
|
});
|
|
312
483
|
},
|
|
484
|
+
async binary(_authorization, request) {
|
|
485
|
+
const ask = pathAddress(request);
|
|
486
|
+
if (ask === null)
|
|
487
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
488
|
+
return downloaded(ask, request.locator);
|
|
489
|
+
},
|
|
490
|
+
async link(_authorization, request) {
|
|
491
|
+
const ask = pathAddress(request);
|
|
492
|
+
if (ask === null)
|
|
493
|
+
return { kind: 'room-not-readable', locator: request.locator };
|
|
494
|
+
return linked(await ask(), request.locator);
|
|
495
|
+
},
|
|
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
|
+
},
|
|
313
525
|
reached(locator, version, target) {
|
|
314
526
|
return reachedVersion(version, target, authorityFor(locator)?.logEpoch);
|
|
315
527
|
},
|
|
@@ -319,7 +531,19 @@ export function createAgentsHouseAdapter(options) {
|
|
|
319
531
|
async action(_authorization, request) {
|
|
320
532
|
return withAt(await houseAction(transport, request));
|
|
321
533
|
},
|
|
322
|
-
roomStream: {
|
|
534
|
+
roomStream: {
|
|
535
|
+
hold: held,
|
|
536
|
+
reopen: notifications.reopen,
|
|
537
|
+
observe(notify, receipts, revisions) {
|
|
538
|
+
heldRevisions = revisions;
|
|
539
|
+
return notifications.observe(notify, receipts);
|
|
540
|
+
},
|
|
541
|
+
published(locator, roomVersion) {
|
|
542
|
+
const at = logPosition(roomVersion);
|
|
543
|
+
if (at !== null)
|
|
544
|
+
notifications.published(locator, at);
|
|
545
|
+
},
|
|
546
|
+
},
|
|
323
547
|
conversations: notifications.conversations,
|
|
324
548
|
serviceMode: notifications.serviceMode,
|
|
325
549
|
};
|
|
@@ -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"}
|