@agentshouse/kit 0.2.1-cbdf76b9a90a3 → 0.2.1-cbff58027f078
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 +28 -28
- package/dist/acp.js +58 -7
- package/dist/afk.js +17 -0
- package/dist/agents.js +55 -32
- package/dist/api.js +1 -1
- package/dist/authority-main.js +2 -2
- package/dist/bridge.js +1 -1
- package/dist/building.js +50 -0
- package/dist/calls.js +71 -0
- package/dist/chunks.js +49 -0
- package/dist/clis.js +45 -40
- package/dist/{stream.js → connection.js} +28 -46
- package/dist/conversations.js +330 -125
- package/dist/copies.js +286 -149
- package/dist/end.js +11 -5
- package/dist/events.js +66 -8
- package/dist/files.js +139 -37
- package/dist/git.js +47 -13
- package/dist/home.js +0 -7
- package/dist/house-cli.js +95 -87
- package/dist/house-main.js +3 -3
- package/dist/instructions.js +29 -9
- package/dist/kit-cli.js +4 -4
- package/dist/kit-main.js +3 -3
- package/dist/login.js +4 -4
- package/dist/mcp.js +6 -2
- package/dist/parts.js +10 -0
- package/dist/placement.js +10 -18
- package/dist/relay.js +21 -0
- package/dist/resident.js +49 -38
- package/dist/scope.js +12 -4
- package/dist/scripts.js +255 -0
- package/dist/service.js +1 -1
- package/dist/sign-in.js +22 -21
- package/dist/silence.js +28 -0
- package/dist/skills.js +42 -97
- package/dist/transcription.js +83 -43
- package/dist/update.js +113 -124
- package/dist/wire.js +23 -5
- package/package.json +20 -21
- package/dist/bootstrap.json +0 -24
- package/dist/clis-main.js +0 -23
- package/dist/inputs.js +0 -63
- package/how-we-work.md +0 -33
- package/skills/ask-matt/LICENSE +0 -21
- package/skills/ask-matt/PHASE-BOUNDARIES.md +0 -55
- package/skills/ask-matt/SKILL.md +0 -97
- package/skills/ask-matt/agents/openai.yaml +0 -5
- 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 -114
- package/skills/codebase-design/agents/openai.yaml +0 -3
- package/skills/diagnosing-bugs/LICENSE +0 -21
- package/skills/diagnosing-bugs/SKILL.md +0 -138
- 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/GLOSSARY-FORMAT.md +0 -60
- package/skills/domain-modeling/LICENSE +0 -21
- package/skills/domain-modeling/SKILL.md +0 -74
- package/skills/domain-modeling/agents/openai.yaml +0 -3
- package/skills/grill-me/LICENSE +0 -21
- package/skills/grill-me/SKILL.md +0 -7
- package/skills/grill-me/agents/openai.yaml +0 -5
- package/skills/grill-with-docs/LICENSE +0 -21
- package/skills/grill-with-docs/SKILL.md +0 -7
- package/skills/grill-with-docs/agents/openai.yaml +0 -5
- 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 -16
- package/skills/handoff/agents/openai.yaml +0 -5
- package/skills/implement/LICENSE +0 -21
- package/skills/implement/SKILL.md +0 -17
- package/skills/implement/agents/openai.yaml +0 -5
- package/skills/implement-spec/LICENSE +0 -21
- package/skills/implement-spec/SKILL.md +0 -40
- package/skills/implement-spec/agents/openai.yaml +0 -5
- 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 -71
- package/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
- package/skills/pr/CREDITS.md +0 -3
- package/skills/pr/LICENSE +0 -21
- package/skills/pr/SKILL.md +0 -170
- package/skills/pr/agents/openai.yaml +0 -3
- package/skills/prototype/LICENSE +0 -21
- package/skills/prototype/LOGIC.md +0 -67
- package/skills/prototype/SKILL.md +0 -26
- 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 -12
- package/skills/research/agents/openai.yaml +0 -3
- package/skills/retro/LICENSE +0 -21
- package/skills/retro/SKILL.md +0 -44
- package/skills/retro/agents/openai.yaml +0 -5
- package/skills/tdd/LICENSE +0 -21
- package/skills/tdd/SKILL.md +0 -38
- 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 -140
- package/skills/teach/agents/openai.yaml +0 -5
- package/skills/to-questionnaire/LICENSE +0 -21
- package/skills/to-questionnaire/SKILL.md +0 -54
- package/skills/to-questionnaire/agents/openai.yaml +0 -5
- package/skills/to-spec/LICENSE +0 -21
- package/skills/to-spec/SKILL.md +0 -75
- package/skills/to-spec/agents/openai.yaml +0 -5
- package/skills/to-tickets/LICENSE +0 -21
- package/skills/to-tickets/SKILL.md +0 -105
- package/skills/to-tickets/agents/openai.yaml +0 -5
- package/skills/triage/AGENT-BRIEF.md +0 -207
- package/skills/triage/LICENSE +0 -21
- package/skills/triage/OUT-OF-SCOPE.md +0 -105
- package/skills/triage/SKILL.md +0 -112
- package/skills/triage/agents/openai.yaml +0 -5
- package/skills/wait-what/LICENSE +0 -21
- package/skills/wait-what/SKILL.md +0 -7
- package/skills/wait-what/agents/openai.yaml +0 -5
- package/skills/wayfinder/LICENSE +0 -21
- package/skills/wayfinder/SKILL.md +0 -128
- package/skills/wayfinder/agents/openai.yaml +0 -5
- package/skills/wizard/LICENSE +0 -21
- package/skills/wizard/SKILL.md +0 -44
- package/skills/wizard/agents/openai.yaml +0 -3
- package/skills/wizard/template.sh +0 -223
- 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 -81
- package/skills/writing-for-agents/agents/openai.yaml +0 -3
package/dist/events.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
const AUTONOMOUS_ORIGINS = new Set(['task-notification', 'peer', 'coordinator', 'observer', 'observer-activity']);
|
|
1
2
|
const COMMAND = { shown: 'command' };
|
|
2
3
|
const HIDDEN = { shown: 'hidden' };
|
|
3
4
|
const FINISHED = new Set(['completed', 'failed']);
|
|
4
5
|
export const RUNNING_JOB = new Set(['running', 'paused']);
|
|
5
|
-
const
|
|
6
|
+
const MESSAGE_TARGETS = new Map([
|
|
7
|
+
['send_agent_function_request', ['agent_function', 'agent']],
|
|
8
|
+
['cancel_agent_function_request', ['request']],
|
|
9
|
+
['send_mail', ['conversation', 'request']],
|
|
10
|
+
]);
|
|
11
|
+
function counted(reports) {
|
|
12
|
+
const whole = reports.every((tokens) => Object.values(tokens).every((count) => Number.isSafeInteger(count) && count >= 0));
|
|
13
|
+
return whole ? reports.map((tokens) => ({ kind: 'tokens', tokens })) : [];
|
|
14
|
+
}
|
|
6
15
|
function words(line) {
|
|
7
16
|
const found = [];
|
|
8
17
|
let word = null;
|
|
@@ -48,11 +57,13 @@ export function houseMessage(line) {
|
|
|
48
57
|
catch {
|
|
49
58
|
return null;
|
|
50
59
|
}
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
if (call === null)
|
|
61
|
+
return null;
|
|
62
|
+
const targets = MESSAGE_TARGETS.get(call.command);
|
|
63
|
+
if (targets === undefined)
|
|
53
64
|
return null;
|
|
54
|
-
const target = call.arguments?.[argument];
|
|
55
|
-
return { shown: 'message', command: call.command, target:
|
|
65
|
+
const target = targets.map((argument) => call.arguments?.[argument]).find((value) => typeof value === 'string');
|
|
66
|
+
return { shown: 'message', command: call.command, target: target ?? null };
|
|
56
67
|
}
|
|
57
68
|
function decidedByLine(command, final) {
|
|
58
69
|
const line = typeof command === 'string' ? command : Array.isArray(command) ? command.at(-1) : undefined;
|
|
@@ -269,6 +280,17 @@ class Reader {
|
|
|
269
280
|
class ClaudeReader extends Reader {
|
|
270
281
|
watchedOptions = { model: 'model' };
|
|
271
282
|
tasks = new Map();
|
|
283
|
+
stop = null;
|
|
284
|
+
answered(result) {
|
|
285
|
+
const models = (result?._meta?.quota?.model_usage ?? []);
|
|
286
|
+
const spent = counted(models.map(({ token_count: count }) => ({
|
|
287
|
+
input: count?.inputTokens,
|
|
288
|
+
output: count?.outputTokens,
|
|
289
|
+
cache_read: count?.cachedInputTokens,
|
|
290
|
+
cache_write: count?.cachedWriteTokens,
|
|
291
|
+
})));
|
|
292
|
+
return [...spent, ...super.answered(result)];
|
|
293
|
+
}
|
|
272
294
|
own(message) {
|
|
273
295
|
if (message.params?.update?._meta?.claudeCode?.parentToolUseId !== undefined)
|
|
274
296
|
return [];
|
|
@@ -276,7 +298,9 @@ class ClaudeReader extends Reader {
|
|
|
276
298
|
}
|
|
277
299
|
update(update) {
|
|
278
300
|
if (update.sessionUpdate === 'usage_update' && update._meta?.['_claude/origin'] !== undefined) {
|
|
279
|
-
|
|
301
|
+
const stop = this.stop;
|
|
302
|
+
this.stop = null;
|
|
303
|
+
return [...super.update(update), { kind: 'ended', stop }];
|
|
280
304
|
}
|
|
281
305
|
if (update.sessionUpdate === 'session_info_update' && update._meta?.jetbrains?.air?.sessionFailure !== undefined) {
|
|
282
306
|
return [{ kind: 'marker', marker: 'retry', text: update._meta.jetbrains.air.sessionFailure.title }];
|
|
@@ -287,6 +311,13 @@ class ClaudeReader extends Reader {
|
|
|
287
311
|
if (message.method !== '_claude/sdkMessage')
|
|
288
312
|
return [];
|
|
289
313
|
const sdk = message.params.message;
|
|
314
|
+
if (sdk.type === 'result') {
|
|
315
|
+
if (AUTONOMOUS_ORIGINS.has(sdk.origin?.kind))
|
|
316
|
+
this.stop = null;
|
|
317
|
+
else
|
|
318
|
+
this.stop = sdk.stop_reason === 'refusal' ? 'refusal' : sdk.is_error === true ? null : 'end_turn';
|
|
319
|
+
return [];
|
|
320
|
+
}
|
|
290
321
|
switch (sdk.subtype) {
|
|
291
322
|
case 'task_started':
|
|
292
323
|
if (sdk.tool_use_id !== undefined)
|
|
@@ -294,6 +325,10 @@ class ClaudeReader extends Reader {
|
|
|
294
325
|
return [];
|
|
295
326
|
case 'task_notification':
|
|
296
327
|
return sdk.tool_use_id === undefined ? [] : this.finish(sdk.tool_use_id, sdk.summary ?? null);
|
|
328
|
+
case 'task_progress':
|
|
329
|
+
if (!this.subagents.has(sdk.tool_use_id) || this.done.has(sdk.tool_use_id) || typeof sdk.description !== 'string')
|
|
330
|
+
return [];
|
|
331
|
+
return [{ kind: 'subagent-activity', id: sdk.tool_use_id, activity: sdk.description }];
|
|
297
332
|
default:
|
|
298
333
|
return [];
|
|
299
334
|
}
|
|
@@ -343,6 +378,20 @@ const COLLABORATION = new Set(['spawnAgent', 'sendInput', 'resumeAgent', 'wait',
|
|
|
343
378
|
class CodexReader extends Reader {
|
|
344
379
|
watchedOptions = { mode: 'mode', collaboration_mode: 'mode' };
|
|
345
380
|
children = new Map();
|
|
381
|
+
answered(result) {
|
|
382
|
+
const usage = result?.usage;
|
|
383
|
+
const spent = usage === null || usage === undefined
|
|
384
|
+
? []
|
|
385
|
+
: counted([
|
|
386
|
+
{
|
|
387
|
+
input: usage.inputTokens,
|
|
388
|
+
output: usage.outputTokens,
|
|
389
|
+
cache_read: usage.cachedReadTokens,
|
|
390
|
+
cache_write: usage.cachedWriteTokens,
|
|
391
|
+
},
|
|
392
|
+
]);
|
|
393
|
+
return [...spent, ...super.answered(result)];
|
|
394
|
+
}
|
|
346
395
|
own(message) {
|
|
347
396
|
if (message.method === 'session/request_permission' && message.params.toolCall?.kind === 'switch_mode') {
|
|
348
397
|
return [{ kind: 'started' }, { kind: 'marker', marker: 'mode', text: message.params.toolCall.title ?? '' }];
|
|
@@ -356,7 +405,7 @@ class CodexReader extends Reader {
|
|
|
356
405
|
if (codex?.threadStatus?.type === 'active')
|
|
357
406
|
return [{ kind: 'started' }];
|
|
358
407
|
if (codex?.threadStatus?.type === 'idle')
|
|
359
|
-
return [{ kind: 'ended' }];
|
|
408
|
+
return [{ kind: 'ended', stop: null }];
|
|
360
409
|
if (codex?.error !== undefined)
|
|
361
410
|
return [{ kind: 'marker', marker: 'retry', text: codex.error.message ?? '' }];
|
|
362
411
|
return [];
|
|
@@ -429,7 +478,9 @@ class GrokReader extends Reader {
|
|
|
429
478
|
return (message.params.runningPromptId ?? null) !== null && session === this.session ? [{ kind: 'started' }] : [];
|
|
430
479
|
}
|
|
431
480
|
if (message.params?.update?.sessionUpdate === 'turn_completed') {
|
|
432
|
-
|
|
481
|
+
if (session !== this.session)
|
|
482
|
+
return [];
|
|
483
|
+
return [...this.spent(message.params.update.usage), { kind: 'ended', stop: null }];
|
|
433
484
|
}
|
|
434
485
|
if (message.method === '_x.ai/models/update') {
|
|
435
486
|
this.offered(message.params);
|
|
@@ -437,6 +488,13 @@ class GrokReader extends Reader {
|
|
|
437
488
|
}
|
|
438
489
|
return super.read(message);
|
|
439
490
|
}
|
|
491
|
+
spent(usage) {
|
|
492
|
+
if (usage === null || usage === undefined)
|
|
493
|
+
return [];
|
|
494
|
+
const { inputTokens, outputTokens, cachedReadTokens, cacheCreationTokens } = usage;
|
|
495
|
+
const input = inputTokens - cachedReadTokens - cacheCreationTokens;
|
|
496
|
+
return counted([{ input, output: outputTokens, cache_read: cachedReadTokens, cache_write: cacheCreationTokens }]);
|
|
497
|
+
}
|
|
440
498
|
offered(models) {
|
|
441
499
|
this.models = models.availableModels ?? this.models;
|
|
442
500
|
this.model = models.currentModelId ?? this.model;
|
package/dist/files.js
CHANGED
|
@@ -1,42 +1,144 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { createWriteStream } from 'node:fs';
|
|
3
|
+
import { mkdir, rename, rm } from 'node:fs/promises';
|
|
4
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
|
+
import { Readable, Transform } from 'node:stream';
|
|
6
|
+
import { pipeline } from 'node:stream/promises';
|
|
7
|
+
import { parseDocument } from 'yaml';
|
|
8
|
+
import { retryDelay } from './api.js';
|
|
9
|
+
import { Unsettled } from './mcp.js';
|
|
5
10
|
import { relayed } from './relay.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
const STALLED_ROUNDS = 8;
|
|
12
|
+
const PART_CHECKSUM = 'x-amz-checksum-sha256';
|
|
13
|
+
const LINK = /\]\((\/rooms\/[^/)\s]+\/[^)\s]+)\)/gu;
|
|
14
|
+
export function linkedPaths(text) {
|
|
15
|
+
return [...new Set(Array.from(text.matchAll(LINK), (link) => link[1]))];
|
|
16
|
+
}
|
|
17
|
+
export function inspectedFile(answer) {
|
|
18
|
+
const document = parseDocument(answer);
|
|
19
|
+
if (document.errors.length > 0)
|
|
20
|
+
return null;
|
|
21
|
+
const inspected = document.toJS();
|
|
22
|
+
if (typeof inspected?.download !== 'string')
|
|
23
|
+
return null;
|
|
24
|
+
return {
|
|
25
|
+
download: inspected.download,
|
|
26
|
+
sha256: String(inspected.sha256),
|
|
27
|
+
media_type: typeof inspected.media_type === 'string' ? inspected.media_type : null,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function paused(milliseconds) {
|
|
31
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
32
|
+
}
|
|
33
|
+
export function storeRefusal(act, status) {
|
|
34
|
+
return new Error(`the ${act} answered ${status}; ${act} the file again`);
|
|
35
|
+
}
|
|
36
|
+
async function partDigests(uploading, partBytes) {
|
|
37
|
+
const whole = createHash('sha256');
|
|
38
|
+
const digests = [];
|
|
39
|
+
for (let start = 0; digests.length === 0 || start < uploading.bytes; start += partBytes) {
|
|
40
|
+
const end = Math.min(start + partBytes, uploading.bytes);
|
|
41
|
+
const digest = createHash('sha256');
|
|
42
|
+
for await (const chunk of uploading.read(start, end)) {
|
|
43
|
+
digest.update(chunk);
|
|
44
|
+
whole.update(chunk);
|
|
45
|
+
}
|
|
46
|
+
digests.push(digest.digest());
|
|
47
|
+
}
|
|
48
|
+
if (whole.digest('hex') !== uploading.sha256)
|
|
49
|
+
throw new Error(`${uploading.name} changed while it was uploaded; upload it again`);
|
|
50
|
+
return digests;
|
|
51
|
+
}
|
|
52
|
+
export async function uploaded(declared, uploading) {
|
|
53
|
+
const partBytes = declared.part_bytes;
|
|
54
|
+
const digests = await partDigests(uploading, partBytes);
|
|
55
|
+
const partSha256 = digests.map((digest) => digest.toString('hex'));
|
|
56
|
+
let answer = await finishing(uploading, declared.id, partSha256);
|
|
57
|
+
let idle = 0;
|
|
58
|
+
let stalled = 0;
|
|
59
|
+
while ('upload' in answer) {
|
|
60
|
+
const { id, parts } = answer.upload;
|
|
61
|
+
let sent = 0;
|
|
62
|
+
let failure = null;
|
|
63
|
+
for (const { part, url } of parts ?? []) {
|
|
64
|
+
const start = (part - 1) * partBytes;
|
|
65
|
+
try {
|
|
66
|
+
await uploading.send(url, start, Math.min(start + partBytes, uploading.bytes), {
|
|
67
|
+
[PART_CHECKSUM]: digests[part - 1].toString('base64'),
|
|
68
|
+
});
|
|
69
|
+
sent++;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
failure = error;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (sent > 0) {
|
|
77
|
+
idle = 0;
|
|
78
|
+
stalled = 0;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (failure !== null && ++stalled >= STALLED_ROUNDS)
|
|
82
|
+
throw failure;
|
|
83
|
+
// A round that sent no part, its store unreachable, is followed by the next finishing call on the jittered curve.
|
|
84
|
+
await paused(retryDelay(idle++));
|
|
85
|
+
}
|
|
86
|
+
answer = await finishing(uploading, id, partSha256);
|
|
87
|
+
}
|
|
88
|
+
return answer;
|
|
89
|
+
}
|
|
90
|
+
async function finishing(uploading, upload, partSha256) {
|
|
91
|
+
for (let unsettled = 1;; unsettled++) {
|
|
92
|
+
try {
|
|
93
|
+
return await uploading.finish(upload, partSha256);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (!(error instanceof Unsettled) || unsettled === STALLED_ROUNDS)
|
|
21
97
|
throw error;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
await writeFile(path, verified(Buffer.from(await answer.arrayBuffer()), file.sha256, file.name));
|
|
26
|
-
paths.push(path);
|
|
98
|
+
}
|
|
99
|
+
// A finishing call House did not settle, unreached or refused short of final, is sent again on the jittered curve as a conversation resends it, so a dropped connection or a House restart resumes the upload instead of ending it.
|
|
100
|
+
await paused(retryDelay(unsettled - 1));
|
|
27
101
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
102
|
+
}
|
|
103
|
+
async function drained(body) {
|
|
104
|
+
for await (const _chunk of body)
|
|
105
|
+
;
|
|
106
|
+
}
|
|
107
|
+
export async function downloaded(address, fetching, destination, name) {
|
|
108
|
+
for (let asked = 1;; asked++) {
|
|
109
|
+
const { download, sha256 } = await address();
|
|
110
|
+
const answer = await fetching(download);
|
|
111
|
+
if (answer.status === 403 && asked === 1) {
|
|
112
|
+
await drained(answer.body);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (answer.status !== 200) {
|
|
116
|
+
await drained(answer.body);
|
|
117
|
+
throw storeRefusal('download', answer.status);
|
|
118
|
+
}
|
|
119
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
120
|
+
const partial = join(dirname(destination), `.${basename(destination)}.${randomUUID()}`);
|
|
121
|
+
const digest = createHash('sha256');
|
|
122
|
+
const hashed = new Transform({
|
|
123
|
+
transform(chunk, _encoding, done) {
|
|
124
|
+
digest.update(chunk);
|
|
125
|
+
done(null, chunk);
|
|
126
|
+
},
|
|
36
127
|
});
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
128
|
+
try {
|
|
129
|
+
await pipeline(answer.body, hashed, createWriteStream(partial, { flags: 'wx' }));
|
|
130
|
+
if (digest.digest('hex') !== sha256)
|
|
131
|
+
throw new Error(`${name} did not arrive whole; download it again`);
|
|
132
|
+
await rename(partial, destination);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
await rm(partial, { force: true });
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function fetched(url, signal) {
|
|
142
|
+
const answer = await fetch(relayed(url), { signal });
|
|
143
|
+
return { status: answer.status, body: answer.body === null ? Readable.from([]) : Readable.fromWeb(answer.body) };
|
|
42
144
|
}
|
package/dist/git.js
CHANGED
|
@@ -2,16 +2,16 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { isUtf8 } from 'node:buffer';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { withoutProxy } from './clis.js';
|
|
6
5
|
import { workspaceRoot } from './home.js';
|
|
6
|
+
import { childEnvironment } from './relay.js';
|
|
7
7
|
export function localCopies() {
|
|
8
8
|
return join(workspaceRoot(), 'local-copies');
|
|
9
9
|
}
|
|
10
10
|
export const HOUSE_REF = 'refs/house/received';
|
|
11
|
-
const COMMITTER = '
|
|
11
|
+
const COMMITTER = 'Kit <house-kit@localhost>';
|
|
12
12
|
export function run(cwd, args, input, env = {}) {
|
|
13
13
|
return new Promise((resolve, reject) => {
|
|
14
|
-
const child = spawn('git', args, { cwd, env: { ...
|
|
14
|
+
const child = spawn('git', args, { cwd, env: { ...childEnvironment(), ...env } });
|
|
15
15
|
const stdout = [];
|
|
16
16
|
let stderr = '';
|
|
17
17
|
child.stdout.on('data', (chunk) => stdout.push(chunk));
|
|
@@ -70,18 +70,52 @@ function data(text) {
|
|
|
70
70
|
const bytes = Buffer.from(text, 'utf8');
|
|
71
71
|
return [Buffer.from(`data ${bytes.length}\n`), bytes, Buffer.from('\n')];
|
|
72
72
|
}
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
function written(stream, bytes) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
stream.write(bytes, (error) => (error ? reject(error) : resolve()));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async function imported(stream, commits) {
|
|
79
|
+
await written(stream, Buffer.from('feature done\n'));
|
|
75
80
|
for (const [index, commit] of commits.entries()) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
await written(stream, Buffer.concat([
|
|
82
|
+
Buffer.from(`commit ${HOUSE_REF}\nmark :${index + 1}\ncommitter ${COMMITTER} ${Math.floor(Date.now() / 1000)} +0000\n`),
|
|
83
|
+
...data(commit.message),
|
|
84
|
+
...(commit.from === null ? [] : [Buffer.from(`from ${commit.from}\n`)]),
|
|
85
|
+
...(commit.merge === undefined ? [] : [Buffer.from(`merge ${commit.merge}\n`)]),
|
|
86
|
+
...(commit.replaces === true ? [Buffer.from('deleteall\n')] : []),
|
|
87
|
+
]));
|
|
88
|
+
for await (const [path, content] of commit.files) {
|
|
89
|
+
await written(stream, content === null
|
|
90
|
+
? Buffer.from(`D ${quoted(path)}\n`)
|
|
91
|
+
: Buffer.concat([Buffer.from(`M 100644 inline ${quoted(path)}\n`), ...data(content)]));
|
|
82
92
|
}
|
|
83
|
-
stream
|
|
93
|
+
await written(stream, Buffer.from('\n'));
|
|
94
|
+
}
|
|
95
|
+
await written(stream, Buffer.from('done\n'));
|
|
96
|
+
}
|
|
97
|
+
export async function importCommits(repository, commits) {
|
|
98
|
+
const child = spawn('git', ['fast-import', '--quiet', '--force'], { cwd: repository, env: childEnvironment() });
|
|
99
|
+
let stderr = '';
|
|
100
|
+
child.stdout.resume();
|
|
101
|
+
child.stderr.on('data', (chunk) => {
|
|
102
|
+
stderr += chunk.toString('utf8');
|
|
103
|
+
});
|
|
104
|
+
child.stdin.on('error', () => undefined);
|
|
105
|
+
const closed = new Promise((resolve, reject) => {
|
|
106
|
+
child.on('error', reject);
|
|
107
|
+
child.on('close', resolve);
|
|
108
|
+
});
|
|
109
|
+
try {
|
|
110
|
+
await imported(child.stdin, commits);
|
|
111
|
+
child.stdin.end();
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
child.stdin.destroy();
|
|
115
|
+
await closed;
|
|
116
|
+
throw error.code === 'EPIPE' ? new Error(`git fast-import failed: ${stderr.trim()}`) : error;
|
|
84
117
|
}
|
|
85
|
-
|
|
118
|
+
if ((await closed) !== 0)
|
|
119
|
+
throw new Error(`git fast-import failed: ${stderr.trim()}`);
|
|
86
120
|
return revision(repository, HOUSE_REF);
|
|
87
121
|
}
|
package/dist/home.js
CHANGED
|
@@ -8,7 +8,6 @@ const ENROLMENT = 'credential.json';
|
|
|
8
8
|
const DISCONNECTED = 'disconnected.json';
|
|
9
9
|
const CONFIGURATION = 'kit.json';
|
|
10
10
|
const OWN_AGENT = 'own-agent.json';
|
|
11
|
-
const RESTART = 'restart';
|
|
12
11
|
let defaulted;
|
|
13
12
|
function writable(directory) {
|
|
14
13
|
try {
|
|
@@ -132,12 +131,6 @@ export async function forgetRejected(rejected) {
|
|
|
132
131
|
export function enrolled() {
|
|
133
132
|
return existsSync(join(kitHome(), ENROLMENT));
|
|
134
133
|
}
|
|
135
|
-
export function restartAsked() {
|
|
136
|
-
return existsSync(join(kitHome(), RESTART));
|
|
137
|
-
}
|
|
138
|
-
export function forgetRestart() {
|
|
139
|
-
return rm(join(kitHome(), RESTART), { force: true });
|
|
140
|
-
}
|
|
141
134
|
export function readOwnAgent() {
|
|
142
135
|
return readHome(OWN_AGENT);
|
|
143
136
|
}
|