@agentshouse/kit 0.2.1-c8179f06883b6 → 0.2.1-c83d468784cc9
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 +37 -36
- package/dist/acp.js +90 -19
- package/dist/afk.js +17 -0
- package/dist/agents.js +154 -48
- package/dist/api.js +5 -5
- package/dist/app-git.js +10 -26
- package/dist/authority-main.js +3 -3
- package/dist/bridge.js +7 -10
- package/dist/building.js +50 -0
- package/dist/calls.js +71 -0
- package/dist/chatgpt.js +32 -0
- package/dist/chunks.js +49 -0
- package/dist/clis.js +57 -42
- package/dist/connection.js +105 -0
- package/dist/conversations.js +572 -182
- package/dist/copies.js +350 -162
- package/dist/end.js +15 -4
- package/dist/events.js +70 -8
- package/dist/files.js +139 -31
- package/dist/git.js +57 -21
- package/dist/home.js +56 -12
- package/dist/house-cli.js +299 -0
- package/dist/house-main.js +7 -228
- package/dist/instructions.js +7 -11
- package/dist/kit-cli.js +68 -0
- package/dist/kit-main.js +8 -67
- package/dist/login.js +43 -19
- package/dist/logout.js +1 -3
- package/dist/mcp.js +6 -2
- package/dist/parts.js +10 -0
- package/dist/placement.js +19 -36
- package/dist/relay.js +21 -0
- package/dist/resident.js +100 -44
- package/dist/scope.js +15 -4
- package/dist/scripts.js +255 -0
- package/dist/secret-input.js +1 -1
- package/dist/service.js +23 -1
- package/dist/sign-in.js +22 -39
- package/dist/silence.js +28 -0
- package/dist/skills.js +49 -35
- package/dist/transcription.js +85 -45
- package/dist/translate.js +2 -8
- package/dist/update.js +113 -126
- package/dist/wire.js +37 -3
- package/dist/work.js +3 -5
- package/package.json +20 -20
- package/dist/bootstrap.json +0 -24
- package/dist/clis-main.js +0 -23
- package/dist/codex.js +0 -61
- package/dist/stream.js +0 -77
- package/how-we-work.md +0 -23
- package/skills/ask-matt/LICENSE +0 -21
- package/skills/ask-matt/PHASE-BOUNDARIES.md +0 -55
- package/skills/ask-matt/SKILL.md +0 -91
- package/skills/ask-matt/agents/openai.yaml +0 -4
- package/skills/code-review/LICENSE +0 -21
- package/skills/code-review/SKILL.md +0 -89
- package/skills/code-review/agents/openai.yaml +0 -3
- package/skills/codebase-design/DEEPENING.md +0 -37
- package/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
- package/skills/codebase-design/LICENSE +0 -21
- package/skills/codebase-design/SKILL.md +0 -116
- package/skills/codebase-design/agents/openai.yaml +0 -3
- package/skills/diagnosing-bugs/LICENSE +0 -21
- package/skills/diagnosing-bugs/SKILL.md +0 -140
- package/skills/diagnosing-bugs/agents/openai.yaml +0 -3
- package/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -44
- package/skills/domain-modeling/ADR-FORMAT.md +0 -47
- package/skills/domain-modeling/CONTEXT-FORMAT.md +0 -60
- package/skills/domain-modeling/LICENSE +0 -21
- package/skills/domain-modeling/SKILL.md +0 -76
- package/skills/domain-modeling/agents/openai.yaml +0 -3
- package/skills/grill-me/LICENSE +0 -21
- package/skills/grill-me/SKILL.md +0 -8
- package/skills/grill-me/agents/openai.yaml +0 -4
- package/skills/grill-with-docs/LICENSE +0 -21
- package/skills/grill-with-docs/SKILL.md +0 -8
- package/skills/grill-with-docs/agents/openai.yaml +0 -4
- package/skills/grilling/LICENSE +0 -21
- package/skills/grilling/SKILL.md +0 -30
- package/skills/grilling/agents/openai.yaml +0 -3
- package/skills/handoff/LICENSE +0 -21
- package/skills/handoff/SKILL.md +0 -17
- package/skills/handoff/agents/openai.yaml +0 -4
- package/skills/implement/LICENSE +0 -21
- package/skills/implement/SKILL.md +0 -16
- package/skills/implement/agents/openai.yaml +0 -4
- package/skills/improve-codebase-architecture/HTML-REPORT.md +0 -123
- package/skills/improve-codebase-architecture/LICENSE +0 -21
- package/skills/improve-codebase-architecture/SKILL.md +0 -72
- package/skills/improve-codebase-architecture/agents/openai.yaml +0 -4
- package/skills/prototype/LICENSE +0 -21
- package/skills/prototype/LOGIC.md +0 -67
- package/skills/prototype/SKILL.md +0 -28
- package/skills/prototype/UI.md +0 -112
- package/skills/prototype/agents/openai.yaml +0 -3
- package/skills/research/LICENSE +0 -21
- package/skills/research/SKILL.md +0 -14
- package/skills/research/agents/openai.yaml +0 -3
- package/skills/resolving-merge-conflicts/LICENSE +0 -21
- package/skills/resolving-merge-conflicts/SKILL.md +0 -16
- package/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
- package/skills/tdd/LICENSE +0 -21
- package/skills/tdd/SKILL.md +0 -40
- package/skills/tdd/agents/openai.yaml +0 -3
- package/skills/tdd/mocking.md +0 -59
- package/skills/tdd/tests.md +0 -77
- package/skills/teach/GLOSSARY-FORMAT.md +0 -35
- package/skills/teach/LEARNING-RECORD-FORMAT.md +0 -46
- package/skills/teach/LICENSE +0 -21
- package/skills/teach/MISSION-FORMAT.md +0 -31
- package/skills/teach/RESOURCES-FORMAT.md +0 -32
- package/skills/teach/SKILL.md +0 -141
- package/skills/teach/agents/openai.yaml +0 -4
- package/skills/to-questionnaire/LICENSE +0 -21
- package/skills/to-questionnaire/SKILL.md +0 -55
- package/skills/to-questionnaire/agents/openai.yaml +0 -4
- package/skills/to-spec/LICENSE +0 -21
- package/skills/to-spec/SKILL.md +0 -76
- package/skills/to-spec/agents/openai.yaml +0 -4
- package/skills/to-tickets/LICENSE +0 -21
- package/skills/to-tickets/SKILL.md +0 -106
- package/skills/to-tickets/agents/openai.yaml +0 -4
- package/skills/wait-what/LICENSE +0 -21
- package/skills/wait-what/SKILL.md +0 -8
- package/skills/wait-what/agents/openai.yaml +0 -4
- package/skills/wayfinder/LICENSE +0 -21
- package/skills/wayfinder/SKILL.md +0 -129
- package/skills/wayfinder/agents/openai.yaml +0 -4
- package/skills/writing-for-agents/LICENSE +0 -21
- package/skills/writing-for-agents/SKILL-MECHANICS.md +0 -22
- package/skills/writing-for-agents/SKILL.md +0 -83
- package/skills/writing-for-agents/agents/openai.yaml +0 -3
package/dist/copies.js
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
import { parse } from '@agentshouse/mdmodel';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { mkdir, readFile, readdir, realpath, rename, rm, rmdir, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
import { finalRefusal, HouseRefusal, retrying } from './api.js';
|
|
6
|
-
import {
|
|
6
|
+
import { chunkTexts, stagedChunks } from './chunks.js';
|
|
7
|
+
import { blobId, blobText, git, isText, HOUSE_REF, importCommits, localCopies, revision, run, succeeded, tree, } from './git.js';
|
|
8
|
+
import { paused } from './files.js';
|
|
7
9
|
import { kitHome } from './home.js';
|
|
8
10
|
import { operationId } from './operation.js';
|
|
9
11
|
import { refusalOf } from './refusals.js';
|
|
10
12
|
import { changed } from './translate.js';
|
|
11
|
-
import { relayed } from './relay.js';
|
|
12
13
|
const BRANCH = 'main';
|
|
13
14
|
const PROVENANCE = '_provenance';
|
|
14
|
-
const IDENTITY = { GIT_COMMITTER_NAME: '
|
|
15
|
+
const IDENTITY = { GIT_COMMITTER_NAME: 'Kit', GIT_COMMITTER_EMAIL: 'house-kit@localhost' };
|
|
16
|
+
const ROOMS = 'rooms/';
|
|
17
|
+
const ENUMERATE = '/kit/door/changes/enumerate';
|
|
18
|
+
const NOT_READY = 'not ready yet; Kit copies it once House has built it';
|
|
19
|
+
const STAGED_CHUNKS = 'house-chunks';
|
|
20
|
+
// A Room answered not ready is asked again on this schedule: its chunk build usually catches up within seconds, so the first asks come soon and the last repeats until it does.
|
|
21
|
+
const ASKED_AGAIN_MS = [1000, 2000, 3000, 5000, 8000, 11_000, 15_000];
|
|
22
|
+
class NotReady extends Error {
|
|
23
|
+
rooms;
|
|
24
|
+
constructor(rooms) {
|
|
25
|
+
super(NOT_READY);
|
|
26
|
+
this.rooms = rooms;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
15
29
|
function causeOf(error) {
|
|
16
30
|
return error instanceof Error ? error.message : String(error);
|
|
17
31
|
}
|
|
32
|
+
function houseCause(error) {
|
|
33
|
+
return error instanceof HouseRefusal ? (refusalOf(error.text)?.text ?? causeOf(error)) : causeOf(error);
|
|
34
|
+
}
|
|
18
35
|
function logged(error) {
|
|
19
36
|
process.stderr.write(`kit: ${causeOf(error)}\n`);
|
|
20
37
|
}
|
|
@@ -29,6 +46,13 @@ const REBASED = new Set([
|
|
|
29
46
|
function rebaseFixes(refusal) {
|
|
30
47
|
return refusal.code === 'edit_conflict' && refusal.conflicts.some((code) => REBASED.has(code));
|
|
31
48
|
}
|
|
49
|
+
function textOnly(paths) {
|
|
50
|
+
const [named, them] = paths.length === 1 ? [`${paths[0]} is`, 'it'] : [`${paths.join(', ')} are`, 'them'];
|
|
51
|
+
return `${named} not text, and house push sends text only; remove ${them} from the commit and place ${them} with house upload`;
|
|
52
|
+
}
|
|
53
|
+
function outside(path) {
|
|
54
|
+
return { refused: true, text: `${path} is not a House Local copy; run house push inside one` };
|
|
55
|
+
}
|
|
32
56
|
function short(commit) {
|
|
33
57
|
return commit.slice(0, 12);
|
|
34
58
|
}
|
|
@@ -48,6 +72,9 @@ function expiredAuthority(error) {
|
|
|
48
72
|
return undefined;
|
|
49
73
|
}
|
|
50
74
|
}
|
|
75
|
+
function grouped(items, size) {
|
|
76
|
+
return items.flatMap((_, index) => (index % size === 0 ? [items.slice(index, index + size)] : []));
|
|
77
|
+
}
|
|
51
78
|
function covers(path, prefix) {
|
|
52
79
|
return prefix === '' || path === prefix || path.startsWith(`${prefix}/`);
|
|
53
80
|
}
|
|
@@ -56,6 +83,12 @@ function sidecar(path) {
|
|
|
56
83
|
segments.splice(-1, 0, PROVENANCE);
|
|
57
84
|
return segments.join('/');
|
|
58
85
|
}
|
|
86
|
+
function recordOf(key) {
|
|
87
|
+
return join(kitHome(), 'local-copies', `${key}.json`);
|
|
88
|
+
}
|
|
89
|
+
function stagingPrefix() {
|
|
90
|
+
return `.${createHash('sha256').update(kitHome()).digest('hex').slice(0, 16)}-`;
|
|
91
|
+
}
|
|
59
92
|
function rootOf(handle) {
|
|
60
93
|
return `/rooms/${handle}`;
|
|
61
94
|
}
|
|
@@ -83,32 +116,59 @@ async function removeFile(repository, path) {
|
|
|
83
116
|
return;
|
|
84
117
|
}
|
|
85
118
|
}
|
|
86
|
-
async function
|
|
119
|
+
async function readOnlyFiles(repository, held) {
|
|
87
120
|
const tracked = new Set((await git(repository, ['ls-files', '-z'])).split('\0'));
|
|
88
121
|
const holding = new Set(held);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
return {
|
|
123
|
+
remove: async (path) => {
|
|
124
|
+
if (!holding.has(path))
|
|
125
|
+
return;
|
|
126
|
+
if (!tracked.has(path))
|
|
127
|
+
await removeFile(repository, path);
|
|
128
|
+
holding.delete(path);
|
|
129
|
+
},
|
|
130
|
+
place: async (path, content) => {
|
|
131
|
+
if (tracked.has(path))
|
|
132
|
+
return;
|
|
133
|
+
if (!holding.has(path) && (await exists(join(repository, path))))
|
|
134
|
+
return;
|
|
135
|
+
await writeText(join(repository, path), content);
|
|
136
|
+
holding.add(path);
|
|
137
|
+
},
|
|
138
|
+
held: () => [...holding].sort(),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async function readOnlyPlaced(repository, held, written, removed) {
|
|
142
|
+
const files = await readOnlyFiles(repository, held);
|
|
143
|
+
for (const path of removed)
|
|
144
|
+
await files.remove(path);
|
|
145
|
+
for (const [path, content] of written)
|
|
146
|
+
await files.place(path, content);
|
|
147
|
+
return files.held();
|
|
148
|
+
}
|
|
149
|
+
async function importedWhole(repository, scope, texts, commit, readOnly) {
|
|
150
|
+
const revisions = {};
|
|
151
|
+
async function* tracked() {
|
|
152
|
+
for await (const { path, content, revision: held, provenance } of texts) {
|
|
153
|
+
if (declaredWritable(path, scope)) {
|
|
154
|
+
revisions[path] = held;
|
|
155
|
+
yield [path, content];
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
await readOnly.place(path, content);
|
|
159
|
+
}
|
|
160
|
+
if (provenance !== undefined)
|
|
161
|
+
await readOnly.place(sidecar(path), provenance);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { house: await importCommits(repository, [{ ...commit, files: tracked() }]), revisions };
|
|
105
165
|
}
|
|
106
166
|
async function ruled(path, rules) {
|
|
107
167
|
if ((await readFile(path, 'utf8').catch(() => null)) !== rules)
|
|
108
168
|
await writeFile(path, rules);
|
|
109
169
|
}
|
|
110
170
|
async function ignoreRules(repository, writable, copy) {
|
|
111
|
-
const readOnly =
|
|
171
|
+
const readOnly = copy.read_only.filter((path) => !path.split('/').includes(PROVENANCE));
|
|
112
172
|
const protectedPath = (path) => copy.protected.some((prefix) => prefix !== '' && covers(path, prefix));
|
|
113
173
|
const whole = copy.protected.filter((prefix) => prefix !== '' && !writable.some((path) => covers(path, prefix)));
|
|
114
174
|
const house = [
|
|
@@ -117,7 +177,7 @@ async function ignoreRules(repository, writable, copy) {
|
|
|
117
177
|
].map(pattern);
|
|
118
178
|
const granted = readOnly.filter((path) => !protectedPath(path)).map(pattern);
|
|
119
179
|
await ruled(join(repository, '.gitignore'), [
|
|
120
|
-
'#
|
|
180
|
+
'# Kit writes this file and .ignore. The paths below are read-only House',
|
|
121
181
|
'# content: Git does not track them, and House refuses a change to them even',
|
|
122
182
|
'# when this file is edited or a path is force-added.',
|
|
123
183
|
'/.gitignore',
|
|
@@ -132,7 +192,7 @@ async function ignoreRules(repository, writable, copy) {
|
|
|
132
192
|
'',
|
|
133
193
|
].join('\n'));
|
|
134
194
|
await ruled(join(repository, '.ignore'), [
|
|
135
|
-
'#
|
|
195
|
+
'# Kit writes this file so native search still reads the read-only House',
|
|
136
196
|
'# content that .gitignore keeps out of Git.',
|
|
137
197
|
`!${PROVENANCE}`,
|
|
138
198
|
...[...house, ...granted].map((rule) => `!${rule}`),
|
|
@@ -168,6 +228,7 @@ export class LocalCopies {
|
|
|
168
228
|
house;
|
|
169
229
|
copies = new Map();
|
|
170
230
|
queues = new Map();
|
|
231
|
+
waiting = new Set();
|
|
171
232
|
answering = Promise.resolve();
|
|
172
233
|
constructor(house) {
|
|
173
234
|
this.house = house;
|
|
@@ -175,7 +236,7 @@ export class LocalCopies {
|
|
|
175
236
|
key: 'owner',
|
|
176
237
|
submit: async (operation, changes) => {
|
|
177
238
|
try {
|
|
178
|
-
const outcome = await retrying(() => house.post('/kit/door/batch', { operation_id: operation, changes }));
|
|
239
|
+
const outcome = await retrying(() => house.post('/kit/door/files/batch', { operation_id: operation, changes }));
|
|
179
240
|
return { accepted: outcome.contents };
|
|
180
241
|
}
|
|
181
242
|
catch (error) {
|
|
@@ -199,70 +260,129 @@ export class LocalCopies {
|
|
|
199
260
|
this.copies.set(copy.key, copy);
|
|
200
261
|
}
|
|
201
262
|
}
|
|
263
|
+
async swept() {
|
|
264
|
+
const rooms = join(localCopies(), 'rooms');
|
|
265
|
+
for (const name of await readdir(rooms).catch(() => [])) {
|
|
266
|
+
if (name.startsWith(stagingPrefix()))
|
|
267
|
+
await rm(join(rooms, name), { recursive: true, force: true });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
202
270
|
async mapping() {
|
|
203
271
|
await this.answering;
|
|
204
272
|
return [...this.copies.values()]
|
|
205
|
-
.filter((copy) => copy.
|
|
273
|
+
.filter((copy) => copy.unfollowed === undefined)
|
|
206
274
|
.map((copy) => `${rootOf(copy.room_handle)}: ${copy.repository}`)
|
|
207
275
|
.sort();
|
|
208
276
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
277
|
+
connected() {
|
|
278
|
+
return this.selected();
|
|
279
|
+
}
|
|
280
|
+
async changed(rooms) {
|
|
281
|
+
if (rooms.length > 0)
|
|
282
|
+
await this.selected(rooms);
|
|
213
283
|
}
|
|
214
284
|
fetch() {
|
|
215
285
|
return this.answered(() => this.fetched(true));
|
|
216
286
|
}
|
|
287
|
+
async selected(rooms) {
|
|
288
|
+
const copied = await this.answered(() => this.fetched(false, rooms));
|
|
289
|
+
if (copied.refused)
|
|
290
|
+
logged(copied.text);
|
|
291
|
+
}
|
|
217
292
|
answered(work) {
|
|
218
293
|
const answered = this.answering.then(work);
|
|
219
294
|
this.answering = answered.catch(() => undefined);
|
|
220
295
|
return answered;
|
|
221
296
|
}
|
|
222
|
-
async fetched(refreshing) {
|
|
223
|
-
const { local_copy: answer } = await this.house.post('/kit/local-
|
|
224
|
-
const synced = new Map(answer.rooms.map((room) => [
|
|
225
|
-
|
|
297
|
+
async fetched(refreshing, rooms) {
|
|
298
|
+
const { local_copy: answer } = await this.house.post('/kit/local-copies/selection/read', rooms === undefined ? {} : { rooms });
|
|
299
|
+
const synced = new Map(answer.rooms.map((room) => [`${ROOMS}${room.room_ref}`, room.room_handle]));
|
|
300
|
+
const named = rooms === undefined ? null : new Set(rooms.map((room) => `${ROOMS}${room}`));
|
|
301
|
+
const held = () => [...this.copies.values()].filter((copy) => named === null || named.has(copy.key));
|
|
302
|
+
return this.holding([...new Set([...held().map((copy) => copy.key), ...synced.keys()])].sort(), async () => {
|
|
226
303
|
const said = [];
|
|
227
304
|
let refused = false;
|
|
228
|
-
|
|
229
|
-
|
|
305
|
+
const mark = refreshing ? 'said' : 'unsaid';
|
|
306
|
+
for (const copy of held()) {
|
|
307
|
+
if (synced.has(copy.key) || copy.unfollowed === 'said' || copy.unfollowed === mark)
|
|
230
308
|
continue;
|
|
231
|
-
await this.save({ ...copy,
|
|
232
|
-
|
|
309
|
+
await this.save({ ...copy, unfollowed: mark });
|
|
310
|
+
if (refreshing)
|
|
311
|
+
said.push(`${rootOf(copy.room_handle)}: no longer followed; kept as it is`);
|
|
233
312
|
}
|
|
234
313
|
const current = [];
|
|
314
|
+
const wanted = new Map();
|
|
235
315
|
for (const [key, handle] of synced) {
|
|
236
316
|
try {
|
|
237
317
|
const held = this.copies.get(key);
|
|
238
318
|
if (held === undefined || !(await exists(held.repository))) {
|
|
239
|
-
|
|
319
|
+
wanted.set(key, handle);
|
|
240
320
|
continue;
|
|
241
321
|
}
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
current.push(copy);
|
|
245
|
-
else
|
|
246
|
-
said.push(...moved(copy, await this.save(await this.received({ ...copy, active: true }))));
|
|
322
|
+
const { unfollowed, ...followed } = held;
|
|
323
|
+
current.push(unfollowed === undefined && held.room_handle === handle ? held : await this.save({ ...followed, room_handle: handle }));
|
|
247
324
|
}
|
|
248
325
|
catch (error) {
|
|
249
326
|
refused = true;
|
|
250
327
|
said.push(`${rootOf(handle)}: ${causeOf(error)}`);
|
|
251
328
|
}
|
|
252
329
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
330
|
+
const unready = [];
|
|
331
|
+
const unsettled = [];
|
|
332
|
+
if (wanted.size > 0) {
|
|
333
|
+
const { authorities } = await this.discovered([...wanted.values()]);
|
|
334
|
+
for (const [key, handle] of wanted) {
|
|
335
|
+
const authority = authorities.find((entry) => `${ROOMS}${entry.room_ref}` === key);
|
|
336
|
+
try {
|
|
337
|
+
if (authority === undefined)
|
|
338
|
+
throw new Error('House serves nothing of it to copy');
|
|
339
|
+
said.push(await this.copied({ key, handle, authority }));
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
if (error instanceof NotReady) {
|
|
343
|
+
unready.push(...error.rooms);
|
|
344
|
+
said.push(`${rootOf(handle)}: ${NOT_READY}`);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
refused = true;
|
|
348
|
+
unsettled.push(key.slice(ROOMS.length));
|
|
349
|
+
said.push(`${rootOf(handle)}: ${causeOf(error)}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
257
352
|
}
|
|
258
|
-
|
|
353
|
+
}
|
|
354
|
+
const reading = refreshing ? current : current.filter((copy) => this.waiting.has(copy.room_ref));
|
|
355
|
+
if (reading.length > 0) {
|
|
356
|
+
const failed = await this.refreshed(reading, unready).then(() => null, houseCause);
|
|
357
|
+
for (const copy of reading) {
|
|
358
|
+
said.push(...(unready.includes(copy.room_ref) ? [`${rootOf(copy.room_handle)}: ${NOT_READY}`] : moved(copy, this.copies.get(copy.key))));
|
|
359
|
+
}
|
|
360
|
+
if (failed !== null) {
|
|
259
361
|
refused = true;
|
|
260
|
-
|
|
362
|
+
unsettled.push(...reading.map((copy) => copy.room_ref));
|
|
363
|
+
said.push(failed);
|
|
261
364
|
}
|
|
262
365
|
}
|
|
366
|
+
for (const room of rooms ?? [...this.waiting]) {
|
|
367
|
+
if (!unready.includes(room) && !unsettled.includes(room))
|
|
368
|
+
this.waiting.delete(room);
|
|
369
|
+
}
|
|
370
|
+
const due = unready.filter((room) => !this.waiting.has(room));
|
|
371
|
+
for (const room of due)
|
|
372
|
+
this.waiting.add(room);
|
|
373
|
+
if (due.length > 0)
|
|
374
|
+
void this.askedAgain(due);
|
|
263
375
|
return { refused, text: said.length === 0 ? 'Nothing new from House.' : said.join('\n') };
|
|
264
376
|
});
|
|
265
377
|
}
|
|
378
|
+
async askedAgain(rooms) {
|
|
379
|
+
let asking = rooms;
|
|
380
|
+
for (let step = 0; asking.length > 0; step++) {
|
|
381
|
+
await paused(ASKED_AGAIN_MS[Math.min(step, ASKED_AGAIN_MS.length - 1)]);
|
|
382
|
+
await this.selected().catch(logged);
|
|
383
|
+
asking = asking.filter((room) => this.waiting.has(room));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
266
386
|
async observed(report) {
|
|
267
387
|
const key = await this.keyAt(dirname(report.common));
|
|
268
388
|
if (key === undefined)
|
|
@@ -291,11 +411,11 @@ export class LocalCopies {
|
|
|
291
411
|
async push(cwd, selector, caller) {
|
|
292
412
|
const located = await run(cwd, ['rev-parse', '--path-format=absolute', '--git-common-dir', '--show-toplevel']);
|
|
293
413
|
if (located.status !== 0)
|
|
294
|
-
return
|
|
414
|
+
return outside(cwd);
|
|
295
415
|
const [common, worktree] = located.stdout.toString('utf8').split('\n');
|
|
296
416
|
const key = await this.keyAt(dirname(common));
|
|
297
417
|
if (key === undefined)
|
|
298
|
-
return
|
|
418
|
+
return outside(worktree);
|
|
299
419
|
return this.exclusive(key, () => this.pushed(key, worktree, selector, caller));
|
|
300
420
|
}
|
|
301
421
|
async keyAt(repository) {
|
|
@@ -316,7 +436,7 @@ export class LocalCopies {
|
|
|
316
436
|
return key === undefined ? work() : this.exclusive(key, () => this.holding(rest, work));
|
|
317
437
|
}
|
|
318
438
|
async save(copy) {
|
|
319
|
-
const path =
|
|
439
|
+
const path = recordOf(copy.key);
|
|
320
440
|
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
321
441
|
const temporary = `${path}.${process.pid}`;
|
|
322
442
|
await writeFile(temporary, JSON.stringify(copy), { mode: 0o600 });
|
|
@@ -324,128 +444,170 @@ export class LocalCopies {
|
|
|
324
444
|
this.copies.set(copy.key, copy);
|
|
325
445
|
return copy;
|
|
326
446
|
}
|
|
327
|
-
async
|
|
328
|
-
const
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
for (const file of view.manifest.files) {
|
|
349
|
-
const content = contents.get(file.path);
|
|
350
|
-
if (file.writable) {
|
|
351
|
-
read.writable.set(file.path, content);
|
|
352
|
-
read.revisions[file.path] = file.revision;
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
read.readOnly.set(file.path, content);
|
|
447
|
+
async discovered(handles) {
|
|
448
|
+
const discover = (group) => this.house.post('/kit/door/rooms/discover', { prefixes: group.map(rootOf) });
|
|
449
|
+
const [first, ...rest] = handles;
|
|
450
|
+
const { bounds, authorities } = await discover([first]);
|
|
451
|
+
for (const group of grouped(rest, bounds.max_prefixes))
|
|
452
|
+
authorities.push(...(await discover(group)).authorities);
|
|
453
|
+
return { bounds, authorities };
|
|
454
|
+
}
|
|
455
|
+
async chunked(root, room, directory) {
|
|
456
|
+
await mkdir(directory, { recursive: true });
|
|
457
|
+
for (let renewed = false;; renewed = true) {
|
|
458
|
+
const { rooms } = await this.house.post('/kit/door/room-chunks/list', { prefixes: [root] });
|
|
459
|
+
const list = rooms.find((entry) => entry.room_ref === room);
|
|
460
|
+
if (list === undefined)
|
|
461
|
+
throw new Error('House serves nothing of it to copy');
|
|
462
|
+
if (!list.ready)
|
|
463
|
+
throw new NotReady([room]);
|
|
464
|
+
const held = new Set(await readdir(directory));
|
|
465
|
+
const failure = await stagedChunks(directory, list.chunks.filter(({ hash }) => !held.has(hash)));
|
|
466
|
+
if (failure === undefined) {
|
|
467
|
+
return { position: list.position, log_epoch: list.log_epoch, texts: chunkTexts(directory, list.chunks, list.models) };
|
|
356
468
|
}
|
|
357
|
-
if (
|
|
358
|
-
|
|
469
|
+
if (renewed && (await readdir(directory)).length === held.size)
|
|
470
|
+
throw failure;
|
|
359
471
|
}
|
|
360
|
-
return read;
|
|
361
472
|
}
|
|
362
|
-
async
|
|
363
|
-
const
|
|
364
|
-
if (read === null)
|
|
365
|
-
throw new Error('House serves nothing of it to copy');
|
|
366
|
-
const repository = join(localCopies(), 'rooms', read.anchor);
|
|
473
|
+
async materialized({ key, handle, authority }, whole) {
|
|
474
|
+
const repository = join(localCopies(), 'rooms', authority.room_ref);
|
|
367
475
|
if (await exists(repository))
|
|
368
|
-
throw new Error(`${repository} already exists and
|
|
369
|
-
const staging = join(dirname(repository),
|
|
476
|
+
throw new Error(`${repository} already exists and Kit leaves it as it is`);
|
|
477
|
+
const staging = join(dirname(repository), `${stagingPrefix()}${randomUUID()}`);
|
|
478
|
+
const scope = { write: authority.operations.write, protected: authority.protected_paths };
|
|
370
479
|
await mkdir(staging, { recursive: true });
|
|
371
|
-
let
|
|
372
|
-
let readOnly;
|
|
480
|
+
let copy;
|
|
373
481
|
try {
|
|
374
482
|
await git(staging, ['init', '-q', '-b', BRANCH]);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
483
|
+
const read = await whole(join(staging, '.git', STAGED_CHUNKS));
|
|
484
|
+
const readOnly = await readOnlyFiles(staging, []);
|
|
485
|
+
const { house, revisions } = await importedWhole(staging, scope, read.texts, { message: `House ${rootOf(handle)}`, from: null }, readOnly);
|
|
378
486
|
await git(staging, ['update-ref', `refs/heads/${BRANCH}`, house]);
|
|
379
487
|
await git(staging, ['reset', '-q', '--hard']);
|
|
380
|
-
|
|
381
|
-
|
|
488
|
+
copy = {
|
|
489
|
+
key,
|
|
490
|
+
room_ref: authority.room_ref,
|
|
491
|
+
room_handle: handle,
|
|
492
|
+
repository,
|
|
493
|
+
house,
|
|
494
|
+
position: read.position,
|
|
495
|
+
log_epoch: read.log_epoch,
|
|
496
|
+
...scope,
|
|
497
|
+
read_only: readOnly.held(),
|
|
498
|
+
manifests: { [house]: { parent: null, files: revisions } },
|
|
499
|
+
moves: [],
|
|
500
|
+
};
|
|
501
|
+
await ignoreRules(staging, Object.keys(revisions), copy);
|
|
502
|
+
await rm(join(staging, '.git', STAGED_CHUNKS), { recursive: true, force: true });
|
|
382
503
|
await rename(staging, repository);
|
|
383
504
|
}
|
|
384
505
|
catch (error) {
|
|
385
506
|
await rm(staging, { recursive: true, force: true });
|
|
386
507
|
throw error;
|
|
387
508
|
}
|
|
388
|
-
|
|
389
|
-
key,
|
|
390
|
-
room_ref: read.anchor,
|
|
391
|
-
room_handle: handle,
|
|
392
|
-
repository,
|
|
393
|
-
active: true,
|
|
394
|
-
house,
|
|
395
|
-
position: read.position,
|
|
396
|
-
log_epoch: read.log_epoch,
|
|
397
|
-
write: read.write,
|
|
398
|
-
protected: read.protected,
|
|
399
|
-
read_only: readOnly,
|
|
400
|
-
manifests: { [house]: { parent: null, files: read.revisions } },
|
|
401
|
-
moves: [],
|
|
402
|
-
});
|
|
403
|
-
return `${rootOf(handle)}: copied to ${repository}`;
|
|
509
|
+
return this.save(copy);
|
|
404
510
|
}
|
|
405
|
-
async
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
511
|
+
async copied(wanted) {
|
|
512
|
+
const { handle, authority } = wanted;
|
|
513
|
+
const root = rootOf(handle);
|
|
514
|
+
let answer = null;
|
|
515
|
+
try {
|
|
516
|
+
if (!this.waiting.has(authority.room_ref)) {
|
|
517
|
+
answer = await this.house.post(ENUMERATE, {
|
|
518
|
+
prefixes: [root],
|
|
519
|
+
positions: [
|
|
520
|
+
{ authority: authority.room_ref, position: '0', log_epoch: authority.log_epoch, page: { through: authority.position, read: 0 } },
|
|
521
|
+
],
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
if (expiredAuthority(error) !== authority.room_ref)
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
529
|
+
const view = answer?.authorities.find((entry) => entry.authority === authority.room_ref);
|
|
530
|
+
if (answer !== null && view === undefined)
|
|
531
|
+
throw new Error('House serves nothing of it to copy');
|
|
532
|
+
if (view !== undefined && view.next === undefined) {
|
|
533
|
+
const texts = view.changes.filter((change) => !('removed' in change));
|
|
534
|
+
const copy = await this.materialized(wanted, async () => ({ position: view.position, log_epoch: view.log_epoch, texts }));
|
|
535
|
+
return `${root}: copied to ${copy.repository}`;
|
|
536
|
+
}
|
|
537
|
+
const copy = await this.materialized(wanted, (directory) => this.chunked(root, authority.room_ref, directory));
|
|
538
|
+
const [caught] = await this.caughtUp([copy], true);
|
|
539
|
+
await succeeded(copy.repository, ['merge', '--ff-only', '-q', caught.house]);
|
|
540
|
+
return `${root}: copied to ${copy.repository}`;
|
|
426
541
|
}
|
|
427
|
-
async
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
542
|
+
async received(copy, authority) {
|
|
543
|
+
const root = rootOf(copy.room_handle);
|
|
544
|
+
const directory = join(copy.repository, '.git', STAGED_CHUNKS);
|
|
545
|
+
const scope = { write: authority.operations.write, protected: authority.protected_paths };
|
|
546
|
+
try {
|
|
547
|
+
const read = await this.chunked(root, copy.room_ref, directory);
|
|
548
|
+
const readOnly = await readOnlyFiles(copy.repository, copy.read_only);
|
|
549
|
+
for (const path of copy.read_only)
|
|
550
|
+
await readOnly.remove(path);
|
|
551
|
+
const commit = { message: `House ${root} at position ${read.position}`, from: copy.house, replaces: true };
|
|
552
|
+
const { house, revisions } = await importedWhole(copy.repository, scope, read.texts, commit, readOnly);
|
|
553
|
+
const next = {
|
|
554
|
+
...copy,
|
|
555
|
+
...scope,
|
|
556
|
+
house,
|
|
557
|
+
position: read.position,
|
|
558
|
+
log_epoch: read.log_epoch,
|
|
559
|
+
read_only: readOnly.held(),
|
|
560
|
+
manifests: { ...copy.manifests, [house]: { parent: null, files: revisions } },
|
|
561
|
+
};
|
|
562
|
+
await ignoreRules(copy.repository, Object.keys(revisions), next);
|
|
563
|
+
return next;
|
|
564
|
+
}
|
|
565
|
+
finally {
|
|
566
|
+
await rm(directory, { recursive: true, force: true });
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
async reread(copy) {
|
|
570
|
+
const { authorities } = await this.discovered([copy.room_handle]);
|
|
571
|
+
const authority = authorities.find((entry) => entry.room_ref === copy.room_ref);
|
|
572
|
+
if (authority === undefined)
|
|
573
|
+
throw new NotReady([copy.room_ref]);
|
|
574
|
+
return this.received(copy, authority);
|
|
575
|
+
}
|
|
576
|
+
async refreshed(copies, unready) {
|
|
577
|
+
const { bounds, authorities } = await this.discovered(copies.map((copy) => copy.room_handle));
|
|
578
|
+
const read = [];
|
|
579
|
+
const unchanged = [];
|
|
432
580
|
for (const copy of copies) {
|
|
433
|
-
const authority =
|
|
581
|
+
const authority = authorities.find((entry) => entry.room_ref === copy.room_ref);
|
|
434
582
|
if (authority !== undefined &&
|
|
435
583
|
JSON.stringify([authority.operations.write, authority.protected_paths]) !== JSON.stringify([copy.write, copy.protected])) {
|
|
436
|
-
|
|
584
|
+
try {
|
|
585
|
+
read.push(await this.save(await this.received(copy, authority)));
|
|
586
|
+
}
|
|
587
|
+
catch (error) {
|
|
588
|
+
if (!(error instanceof NotReady))
|
|
589
|
+
throw error;
|
|
590
|
+
unready.push(...error.rooms);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
unchanged.push(copy);
|
|
437
595
|
}
|
|
438
596
|
}
|
|
439
|
-
const
|
|
440
|
-
|
|
597
|
+
for (const group of grouped(unchanged, bounds.max_prefixes))
|
|
598
|
+
await this.caughtUp(group, false, unready);
|
|
599
|
+
for (const group of grouped(read, bounds.max_prefixes))
|
|
600
|
+
await this.caughtUp(group, true, unready);
|
|
441
601
|
}
|
|
442
|
-
async caughtUp(copies, whole) {
|
|
602
|
+
async caughtUp(copies, whole, unready) {
|
|
443
603
|
const caught = new Map(copies.map((copy) => [copy.key, copy]));
|
|
604
|
+
const read = new Set();
|
|
605
|
+
const waiting = unready ?? [];
|
|
444
606
|
let asking = [...copies];
|
|
445
607
|
while (asking.length > 0) {
|
|
446
608
|
let answer;
|
|
447
609
|
try {
|
|
448
|
-
answer = await this.house.post(
|
|
610
|
+
answer = await this.house.post(ENUMERATE, {
|
|
449
611
|
prefixes: asking.map((copy) => rootOf(copy.room_handle)),
|
|
450
612
|
positions: asking.map((copy) => ({ authority: copy.room_ref, position: copy.position, log_epoch: copy.log_epoch })),
|
|
451
613
|
});
|
|
@@ -454,8 +616,22 @@ export class LocalCopies {
|
|
|
454
616
|
const expired = asking.find((copy) => copy.room_ref === expiredAuthority(error));
|
|
455
617
|
if (expired === undefined)
|
|
456
618
|
throw error;
|
|
457
|
-
caught.set(expired.key, await this.save(await this.received(expired)));
|
|
458
619
|
asking = asking.filter((copy) => copy !== expired);
|
|
620
|
+
if (read.has(expired.key)) {
|
|
621
|
+
waiting.push(expired.room_ref);
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
read.add(expired.key);
|
|
625
|
+
try {
|
|
626
|
+
const next = await this.save(await this.reread(expired));
|
|
627
|
+
caught.set(expired.key, next);
|
|
628
|
+
asking.push(next);
|
|
629
|
+
}
|
|
630
|
+
catch (refused) {
|
|
631
|
+
if (!(refused instanceof NotReady))
|
|
632
|
+
throw refused;
|
|
633
|
+
waiting.push(expired.room_ref);
|
|
634
|
+
}
|
|
459
635
|
continue;
|
|
460
636
|
}
|
|
461
637
|
const advanced = [];
|
|
@@ -467,8 +643,10 @@ export class LocalCopies {
|
|
|
467
643
|
caught.set(copy.key, next);
|
|
468
644
|
advanced.push(next);
|
|
469
645
|
}
|
|
470
|
-
asking =
|
|
646
|
+
asking = advanced.filter((copy) => whole || read.has(copy.key));
|
|
471
647
|
}
|
|
648
|
+
if (unready === undefined && waiting.length > 0)
|
|
649
|
+
throw new NotReady(waiting);
|
|
472
650
|
return copies.map((copy) => caught.get(copy.key));
|
|
473
651
|
}
|
|
474
652
|
async incorporated(copy, changes, position, logEpoch) {
|
|
@@ -532,9 +710,6 @@ export class LocalCopies {
|
|
|
532
710
|
}
|
|
533
711
|
copy = await this.save(without(copy));
|
|
534
712
|
}
|
|
535
|
-
if (!copy.active) {
|
|
536
|
-
return { refused: true, text: `${rootOf(copy.room_handle)} is no longer synced to this Environment, so House takes no push from it` };
|
|
537
|
-
}
|
|
538
713
|
const listed = (await git(copy.repository, ['rev-list', '--topo-order', commit])).split('\n');
|
|
539
714
|
const base = listed.find((ancestor) => ancestor in copy.manifests);
|
|
540
715
|
if (base === undefined) {
|
|
@@ -545,7 +720,7 @@ export class LocalCopies {
|
|
|
545
720
|
changes = await this.derived(copy, base, commit);
|
|
546
721
|
}
|
|
547
722
|
catch (error) {
|
|
548
|
-
return { refused: true, text:
|
|
723
|
+
return { refused: true, text: houseCause(error) };
|
|
549
724
|
}
|
|
550
725
|
if (changes.length === 0)
|
|
551
726
|
return { refused: false, text: `Nothing to submit: ${short(commit)} matches House.` };
|
|
@@ -572,7 +747,18 @@ export class LocalCopies {
|
|
|
572
747
|
const after = await tree(copy.repository, commit);
|
|
573
748
|
const moved = movedPaths(copy.moves, before, after);
|
|
574
749
|
const arrived = new Set(moved.values());
|
|
575
|
-
const
|
|
750
|
+
const arriving = [...after].filter(([path, entry]) => before.get(path) !== entry);
|
|
751
|
+
if (arriving.length > 0) {
|
|
752
|
+
const { bounds } = await this.house.post('/kit/door/rooms/discover', { prefixes: [root] });
|
|
753
|
+
const binary = [];
|
|
754
|
+
for (const [path, entry] of arriving) {
|
|
755
|
+
if (!(await isText(copy.repository, entry, bounds.max_authored_file_bytes)))
|
|
756
|
+
binary.push(path);
|
|
757
|
+
}
|
|
758
|
+
if (binary.length > 0)
|
|
759
|
+
throw new Error(textOnly(binary));
|
|
760
|
+
}
|
|
761
|
+
const text = (entry) => blobText(copy.repository, entry);
|
|
576
762
|
const changes = [];
|
|
577
763
|
for (const path of [...new Set([...before.keys(), ...after.keys()])].sort()) {
|
|
578
764
|
if (arrived.has(path))
|
|
@@ -585,7 +771,7 @@ export class LocalCopies {
|
|
|
585
771
|
changes.push({ op: 'rename', path: `${root}/${path}`, to: `${root}/${to}`, base: known });
|
|
586
772
|
const moving = after.get(to);
|
|
587
773
|
if (moving !== prior) {
|
|
588
|
-
changes.push({ op: 'replace', path: `${root}/${to}`, base: known, content: await text(moving
|
|
774
|
+
changes.push({ op: 'replace', path: `${root}/${to}`, base: known, content: await text(moving) });
|
|
589
775
|
}
|
|
590
776
|
continue;
|
|
591
777
|
}
|
|
@@ -594,9 +780,9 @@ export class LocalCopies {
|
|
|
594
780
|
if (next === undefined)
|
|
595
781
|
changes.push({ op: 'remove', path: `${root}/${path}`, base: known });
|
|
596
782
|
else if (prior === undefined)
|
|
597
|
-
changes.push({ op: 'create', path: `${root}/${path}`, content: await text(next
|
|
783
|
+
changes.push({ op: 'create', path: `${root}/${path}`, content: await text(next) });
|
|
598
784
|
else
|
|
599
|
-
changes.push(...changed(`${root}/${path}`, path, known, await text(prior
|
|
785
|
+
changes.push(...changed(`${root}/${path}`, path, known, await text(prior), await text(next)));
|
|
600
786
|
}
|
|
601
787
|
return changes;
|
|
602
788
|
}
|
|
@@ -625,8 +811,10 @@ export class LocalCopies {
|
|
|
625
811
|
}
|
|
626
812
|
if (outcome.refused.code === 'expired')
|
|
627
813
|
return this.recovered(copy);
|
|
628
|
-
const rebased = rebaseFixes(outcome.refused);
|
|
629
814
|
let settled = await this.save(without(copy));
|
|
815
|
+
if (settled.unfollowed !== undefined)
|
|
816
|
+
return { refused: true, text: outcome.refused.text };
|
|
817
|
+
const rebased = rebaseFixes(outcome.refused);
|
|
630
818
|
try {
|
|
631
819
|
settled = (await this.caughtUp([settled], true))[0];
|
|
632
820
|
}
|
|
@@ -663,7 +851,7 @@ export class LocalCopies {
|
|
|
663
851
|
written.push({
|
|
664
852
|
path: `${root}/${path}`,
|
|
665
853
|
revision: entry === undefined ? null : (revisionAt(current, current.house, path) ?? null),
|
|
666
|
-
content: entry === undefined ? null : await blobText(copy.repository, entry
|
|
854
|
+
content: entry === undefined ? null : await blobText(copy.repository, entry),
|
|
667
855
|
});
|
|
668
856
|
}
|
|
669
857
|
return this.finished(await this.save({ ...current, push: { ...push, state: 'accepted', written } }));
|
|
@@ -707,7 +895,7 @@ export class LocalCopies {
|
|
|
707
895
|
{
|
|
708
896
|
path: `${rootOf(copy.room_handle)}/${path}`,
|
|
709
897
|
revision: entry === undefined ? null : (revisionAt(copy, copy.house, path) ?? null),
|
|
710
|
-
content: entry === undefined ? null : await blobText(copy.repository, entry
|
|
898
|
+
content: entry === undefined ? null : await blobText(copy.repository, entry),
|
|
711
899
|
},
|
|
712
900
|
];
|
|
713
901
|
}
|
|
@@ -752,7 +940,7 @@ export class LocalCopies {
|
|
|
752
940
|
async landed(repository, changes, house, selected) {
|
|
753
941
|
const text = async (path) => {
|
|
754
942
|
const entry = house.get(relative(path));
|
|
755
|
-
return entry === undefined ? undefined : blobText(repository, entry
|
|
943
|
+
return entry === undefined ? undefined : blobText(repository, entry);
|
|
756
944
|
};
|
|
757
945
|
for (const change of changes) {
|
|
758
946
|
const current = await text(change.path);
|
|
@@ -778,7 +966,7 @@ export class LocalCopies {
|
|
|
778
966
|
return true;
|
|
779
967
|
}
|
|
780
968
|
async revisionText(path, wanted) {
|
|
781
|
-
const page = await this.house.post('/kit/door/revisions', {
|
|
969
|
+
const page = await this.house.post('/kit/door/files/revisions/list', {
|
|
782
970
|
path,
|
|
783
971
|
revision: wanted,
|
|
784
972
|
});
|