@agentshouse/kit 0.2.1-cca9e1f43de44 → 0.2.1-ccf188e02985d
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 +30 -28
- package/dist/acp.js +41 -111
- package/dist/agents.js +110 -22
- package/dist/api.js +9 -5
- package/dist/app-git.js +10 -26
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +10 -24
- package/dist/chatgpt.js +32 -0
- package/dist/clis.js +95 -14
- package/dist/codex-main.js +58 -0
- package/dist/conversations.js +181 -114
- package/dist/copies.js +188 -119
- package/dist/end.js +19 -4
- package/dist/enrol-main.js +7 -1
- package/dist/events.js +14 -76
- package/dist/files.js +8 -2
- package/dist/git.js +10 -8
- package/dist/home.js +96 -7
- package/dist/house-cli.js +291 -0
- package/dist/house-main.js +7 -177
- package/dist/inputs.js +37 -0
- package/dist/instructions.js +10 -32
- package/dist/kit-cli.js +68 -0
- package/dist/kit-main.js +8 -22
- package/dist/login.js +117 -106
- package/dist/logout.js +14 -16
- package/dist/parts.js +34 -16
- package/dist/placement.js +30 -0
- package/dist/relay.js +12 -2
- package/dist/resident.js +89 -33
- package/dist/scope.js +203 -0
- package/dist/service-main.js +9 -0
- package/dist/service.js +55 -0
- package/dist/sign-in.js +6 -24
- package/dist/skills.js +81 -12
- package/dist/stream.js +68 -17
- package/dist/transcription.js +3 -3
- package/dist/update.js +48 -29
- package/dist/wire.js +6 -0
- package/dist/work.js +346 -0
- package/how-we-work.md +13 -3
- package/package.json +3 -2
- package/skills/ask-matt/SKILL.md +17 -11
- package/skills/ask-matt/agents/openai.yaml +1 -0
- package/skills/code-review/SKILL.md +5 -5
- package/skills/codebase-design/DESIGN-IT-TWICE.md +1 -1
- package/skills/codebase-design/SKILL.md +0 -2
- package/skills/diagnosing-bugs/SKILL.md +2 -4
- package/skills/domain-modeling/{CONTEXT-FORMAT.md → GLOSSARY-FORMAT.md} +10 -10
- package/skills/domain-modeling/SKILL.md +12 -14
- package/skills/grill-me/SKILL.md +1 -2
- package/skills/grill-me/agents/openai.yaml +1 -0
- package/skills/grill-with-docs/SKILL.md +1 -2
- package/skills/grill-with-docs/agents/openai.yaml +1 -0
- package/skills/grilling/SKILL.md +2 -2
- package/skills/handoff/SKILL.md +2 -3
- package/skills/handoff/agents/openai.yaml +1 -0
- package/skills/implement/SKILL.md +5 -4
- package/skills/implement/agents/openai.yaml +1 -0
- package/skills/implement-spec/SKILL.md +40 -0
- package/skills/implement-spec/agents/openai.yaml +5 -0
- package/skills/improve-codebase-architecture/SKILL.md +6 -7
- package/skills/improve-codebase-architecture/agents/openai.yaml +1 -0
- package/skills/pr/CREDITS.md +3 -0
- package/skills/pr/LICENSE +21 -0
- package/skills/pr/SKILL.md +170 -0
- package/skills/pr/agents/openai.yaml +3 -0
- package/skills/prototype/SKILL.md +0 -2
- package/skills/research/SKILL.md +0 -2
- package/skills/retro/LICENSE +21 -0
- package/skills/retro/SKILL.md +44 -0
- package/skills/retro/agents/openai.yaml +5 -0
- package/skills/tdd/SKILL.md +2 -4
- package/skills/teach/SKILL.md +3 -4
- package/skills/teach/agents/openai.yaml +1 -0
- package/skills/to-questionnaire/SKILL.md +1 -2
- package/skills/to-questionnaire/agents/openai.yaml +1 -0
- package/skills/to-spec/SKILL.md +1 -2
- package/skills/to-spec/agents/openai.yaml +1 -0
- package/skills/to-tickets/SKILL.md +3 -4
- package/skills/to-tickets/agents/openai.yaml +1 -0
- package/skills/triage/AGENT-BRIEF.md +207 -0
- package/skills/triage/LICENSE +21 -0
- package/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/skills/triage/SKILL.md +112 -0
- package/skills/triage/agents/openai.yaml +5 -0
- package/skills/wait-what/SKILL.md +2 -3
- package/skills/wait-what/agents/openai.yaml +1 -0
- package/skills/wayfinder/SKILL.md +5 -6
- package/skills/wayfinder/agents/openai.yaml +1 -0
- package/skills/wizard/LICENSE +21 -0
- package/skills/wizard/SKILL.md +44 -0
- package/skills/wizard/agents/openai.yaml +3 -0
- package/skills/wizard/template.sh +223 -0
- package/skills/writing-for-agents/SKILL.md +0 -2
- package/skills/resolving-merge-conflicts/SKILL.md +0 -16
- package/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
- /package/skills/{resolving-merge-conflicts → implement-spec}/LICENSE +0 -0
package/dist/events.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const COMMAND = { shown: 'command' };
|
|
2
2
|
const HIDDEN = { shown: 'hidden' };
|
|
3
3
|
const FINISHED = new Set(['completed', 'failed']);
|
|
4
|
-
const RUNNING_JOB = new Set(['running', 'paused']);
|
|
4
|
+
export const RUNNING_JOB = new Set(['running', 'paused']);
|
|
5
5
|
const ROUTINE_TARGETS = { send_routine_request: 'access', cancel_routine_request: 'request' };
|
|
6
6
|
function words(line) {
|
|
7
7
|
const found = [];
|
|
@@ -113,9 +113,6 @@ class Reader {
|
|
|
113
113
|
refused(option) {
|
|
114
114
|
this.requestedOptions.delete(option);
|
|
115
115
|
}
|
|
116
|
-
turnStarted() {
|
|
117
|
-
return [];
|
|
118
|
-
}
|
|
119
116
|
closeTurn() {
|
|
120
117
|
const events = [];
|
|
121
118
|
for (const [id, call] of this.calls) {
|
|
@@ -187,10 +184,7 @@ class Reader {
|
|
|
187
184
|
const label = this.jobs.get(id) ?? '';
|
|
188
185
|
if (state !== 'running')
|
|
189
186
|
this.jobs.delete(id);
|
|
190
|
-
return [
|
|
191
|
-
{ kind: 'busy', key: `job:${id}`, running: state === 'running' },
|
|
192
|
-
{ kind: 'marker', marker: 'job', text: `${label}: ${state}` },
|
|
193
|
-
];
|
|
187
|
+
return [{ kind: 'marker', marker: 'job', text: `${label}: ${state}` }];
|
|
194
188
|
}
|
|
195
189
|
optionsChanged(options) {
|
|
196
190
|
const events = [];
|
|
@@ -275,7 +269,6 @@ class Reader {
|
|
|
275
269
|
class ClaudeReader extends Reader {
|
|
276
270
|
watchedOptions = { model: 'model' };
|
|
277
271
|
tasks = new Map();
|
|
278
|
-
scheduled = new Map();
|
|
279
272
|
own(message) {
|
|
280
273
|
if (message.params?.update?._meta?.claudeCode?.parentToolUseId !== undefined)
|
|
281
274
|
return [];
|
|
@@ -295,10 +288,6 @@ class ClaudeReader extends Reader {
|
|
|
295
288
|
return [];
|
|
296
289
|
const sdk = message.params.message;
|
|
297
290
|
switch (sdk.subtype) {
|
|
298
|
-
case 'background_tasks_changed':
|
|
299
|
-
return [{ kind: 'busy', key: 'tasks', running: sdk.tasks.length > 0 }];
|
|
300
|
-
case 'session_state_changed':
|
|
301
|
-
return [{ kind: 'busy', key: 'state', running: sdk.state === 'running' }];
|
|
302
291
|
case 'task_started':
|
|
303
292
|
if (sdk.tool_use_id !== undefined)
|
|
304
293
|
this.tasks.set(sdk.task_id, sdk.tool_use_id);
|
|
@@ -309,16 +298,6 @@ class ClaudeReader extends Reader {
|
|
|
309
298
|
return [];
|
|
310
299
|
}
|
|
311
300
|
}
|
|
312
|
-
turnStarted() {
|
|
313
|
-
const events = [];
|
|
314
|
-
for (const [id, entry] of this.scheduled) {
|
|
315
|
-
if (entry.recurring)
|
|
316
|
-
continue;
|
|
317
|
-
this.scheduled.delete(id);
|
|
318
|
-
events.push({ kind: 'busy', key: `scheduled:${id}`, running: false });
|
|
319
|
-
}
|
|
320
|
-
return events;
|
|
321
|
-
}
|
|
322
301
|
decide(call, final) {
|
|
323
302
|
const tool = call.meta.claudeCode?.toolName;
|
|
324
303
|
if (tool === 'Agent' || tool === 'Task' || call.meta.jetbrains?.air?.subagent === true) {
|
|
@@ -350,22 +329,7 @@ class ClaudeReader extends Reader {
|
|
|
350
329
|
if (call.status !== 'completed')
|
|
351
330
|
return [];
|
|
352
331
|
if (tool === 'CronCreate' || (tool === 'ScheduleWakeup' && call.rawInput?.stop !== true)) {
|
|
353
|
-
|
|
354
|
-
recurring: tool === 'CronCreate' && call.rawInput?.recurring !== false,
|
|
355
|
-
text: contentText(call.content) ?? '',
|
|
356
|
-
});
|
|
357
|
-
return [
|
|
358
|
-
{ kind: 'busy', key: `scheduled:${call.id}`, running: true },
|
|
359
|
-
{ kind: 'marker', marker: 'job', text: `${call.rawInput?.prompt ?? call.title ?? ''}: scheduled` },
|
|
360
|
-
];
|
|
361
|
-
}
|
|
362
|
-
if (tool === 'CronDelete') {
|
|
363
|
-
for (const [id, entry] of this.scheduled) {
|
|
364
|
-
if (!entry.text.includes(String(call.rawInput?.id)))
|
|
365
|
-
continue;
|
|
366
|
-
this.scheduled.delete(id);
|
|
367
|
-
return [{ kind: 'busy', key: `scheduled:${id}`, running: false }];
|
|
368
|
-
}
|
|
332
|
+
return [{ kind: 'marker', marker: 'job', text: `${call.rawInput?.prompt ?? call.title ?? ''}: scheduled` }];
|
|
369
333
|
}
|
|
370
334
|
return [];
|
|
371
335
|
}
|
|
@@ -402,15 +366,9 @@ class CodexReader extends Reader {
|
|
|
402
366
|
case 'subagent_spawned':
|
|
403
367
|
this.children.set(update.subagentSessionId, { id: null, text: '' });
|
|
404
368
|
this.subagents.add(update.subagentSessionId);
|
|
405
|
-
return [
|
|
406
|
-
{ kind: 'busy', key: `subagent:${update.subagentSessionId}`, running: true },
|
|
407
|
-
{ kind: 'subagent', id: update.subagentSessionId, description: update.name },
|
|
408
|
-
];
|
|
369
|
+
return [{ kind: 'subagent', id: update.subagentSessionId, description: update.name }];
|
|
409
370
|
case 'subagent_state_update':
|
|
410
|
-
return
|
|
411
|
-
{ kind: 'busy', key: `subagent:${update.subagentSessionId}`, running: false },
|
|
412
|
-
...this.finish(update.subagentSessionId, this.children.get(update.subagentSessionId)?.text || null),
|
|
413
|
-
];
|
|
371
|
+
return this.finish(update.subagentSessionId, this.children.get(update.subagentSessionId)?.text || null);
|
|
414
372
|
default:
|
|
415
373
|
return super.update(update);
|
|
416
374
|
}
|
|
@@ -427,10 +385,6 @@ class CodexReader extends Reader {
|
|
|
427
385
|
child.id = id;
|
|
428
386
|
child.text += update.content.text;
|
|
429
387
|
}
|
|
430
|
-
const status = update._meta?.codex?.threadStatus?.type;
|
|
431
|
-
if (update.sessionUpdate === 'session_info_update' && status !== undefined) {
|
|
432
|
-
return [{ kind: 'busy', key: `thread:${session}`, running: status === 'active' }];
|
|
433
|
-
}
|
|
434
388
|
return [];
|
|
435
389
|
}
|
|
436
390
|
decide(call) {
|
|
@@ -460,7 +414,6 @@ class CodexReader extends Reader {
|
|
|
460
414
|
class GrokReader extends Reader {
|
|
461
415
|
watchedOptions = {};
|
|
462
416
|
children = new Map();
|
|
463
|
-
queues = new Map();
|
|
464
417
|
models = [];
|
|
465
418
|
model = null;
|
|
466
419
|
answered(result) {
|
|
@@ -473,15 +426,10 @@ class GrokReader extends Reader {
|
|
|
473
426
|
if (this.session !== null && session !== undefined && session !== this.session && !this.children.has(session))
|
|
474
427
|
return [];
|
|
475
428
|
if (message.method === '_x.ai/queue/changed') {
|
|
476
|
-
|
|
477
|
-
this.queues.set(session, { running, entries: message.params.entries.length });
|
|
478
|
-
return [this.queued(session), ...(running !== null && session === this.session ? [{ kind: 'started' }] : [])];
|
|
429
|
+
return (message.params.runningPromptId ?? null) !== null && session === this.session ? [{ kind: 'started' }] : [];
|
|
479
430
|
}
|
|
480
431
|
if (message.params?.update?.sessionUpdate === 'turn_completed') {
|
|
481
|
-
|
|
482
|
-
if (queue !== undefined && queue.running === message.params.update.prompt_id)
|
|
483
|
-
queue.running = null;
|
|
484
|
-
return [this.queued(session), ...(session === this.session ? [{ kind: 'ended' }] : [])];
|
|
432
|
+
return session === this.session ? [{ kind: 'ended' }] : [];
|
|
485
433
|
}
|
|
486
434
|
if (message.method === '_x.ai/models/update') {
|
|
487
435
|
this.offered(message.params);
|
|
@@ -489,10 +437,6 @@ class GrokReader extends Reader {
|
|
|
489
437
|
}
|
|
490
438
|
return super.read(message);
|
|
491
439
|
}
|
|
492
|
-
queued(session) {
|
|
493
|
-
const queue = this.queues.get(session);
|
|
494
|
-
return { kind: 'busy', key: `queue:${session}`, running: queue !== undefined && (queue.running !== null || queue.entries > 0) };
|
|
495
|
-
}
|
|
496
440
|
offered(models) {
|
|
497
441
|
this.models = models.availableModels ?? this.models;
|
|
498
442
|
this.model = models.currentModelId ?? this.model;
|
|
@@ -508,16 +452,9 @@ class GrokReader extends Reader {
|
|
|
508
452
|
case '_x.ai/task_completed':
|
|
509
453
|
return this.job(update.task_snapshot.task_id, null, update.task_snapshot.exit_code === 0 ? 'completed' : 'failed');
|
|
510
454
|
case '_x.ai/scheduled_task_created':
|
|
511
|
-
return [
|
|
512
|
-
{ kind: 'busy', key: `scheduled:${update.task_id}`, running: true },
|
|
513
|
-
{ kind: 'marker', marker: 'job', text: `${update.prompt}: next run ${update.next_fire_at}` },
|
|
514
|
-
];
|
|
515
|
-
case '_x.ai/scheduled_task_deleted':
|
|
516
|
-
return [{ kind: 'busy', key: `scheduled:${update.task_id}`, running: false }];
|
|
455
|
+
return [{ kind: 'marker', marker: 'job', text: `${update.prompt}: next run ${update.next_fire_at}` }];
|
|
517
456
|
case '_x.ai/session_notification':
|
|
518
457
|
return this.notification(update);
|
|
519
|
-
case '_x.ai/mcp_initialized':
|
|
520
|
-
return [{ kind: 'servers' }];
|
|
521
458
|
default:
|
|
522
459
|
return [];
|
|
523
460
|
}
|
|
@@ -538,14 +475,11 @@ class GrokReader extends Reader {
|
|
|
538
475
|
candidate.decided.description === update.description) ??
|
|
539
476
|
[...this.calls.values()].find((candidate) => candidate.decided?.shown === 'subagent' && ![...this.children.values()].includes(candidate.id));
|
|
540
477
|
this.children.set(update.child_session_id, call?.id ?? null);
|
|
541
|
-
return [
|
|
478
|
+
return [];
|
|
542
479
|
}
|
|
543
480
|
case 'subagent_finished': {
|
|
544
481
|
const call = this.children.get(update.child_session_id);
|
|
545
|
-
return [
|
|
546
|
-
{ kind: 'busy', key: `subagent:${update.child_session_id}`, running: false },
|
|
547
|
-
...(typeof call === 'string' ? this.finish(call, update.output ?? null) : []),
|
|
548
|
-
];
|
|
482
|
+
return typeof call === 'string' ? this.finish(call, update.output ?? null) : [];
|
|
549
483
|
}
|
|
550
484
|
case 'auto_compact_completed':
|
|
551
485
|
return [{ kind: 'marker', marker: 'compaction', text: '' }];
|
|
@@ -578,6 +512,10 @@ class GrokReader extends Reader {
|
|
|
578
512
|
return [];
|
|
579
513
|
}
|
|
580
514
|
}
|
|
515
|
+
export function answeredFailure(answer) {
|
|
516
|
+
const failure = answer._meta?.jetbrains?.air?.sessionFailure;
|
|
517
|
+
return typeof failure?.title === 'string' ? { title: failure.title, limit: failure.category === 'limit' } : null;
|
|
518
|
+
}
|
|
581
519
|
export function readerFor(kind) {
|
|
582
520
|
if (kind === 'claude-agent-acp')
|
|
583
521
|
return new ClaudeReader();
|
package/dist/files.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { HouseRefusal, retrying } from './api.js';
|
|
4
5
|
import { relayed } from './relay.js';
|
|
6
|
+
export function verified(bytes, sha256, name) {
|
|
7
|
+
if (createHash('sha256').update(bytes).digest('hex') !== sha256)
|
|
8
|
+
throw new Error(`${name} did not arrive whole; download it again`);
|
|
9
|
+
return bytes;
|
|
10
|
+
}
|
|
5
11
|
export async function placeFiles(house, directory, files, signal) {
|
|
6
12
|
const paths = [];
|
|
7
13
|
for (const file of files) {
|
|
8
|
-
const original = await house.post('/kit/conversation/originals/get', { version: file.version, download: true }, signal);
|
|
14
|
+
const original = await retrying(() => house.post('/kit/conversation/originals/get', { version: file.version, download: true }, signal), signal);
|
|
9
15
|
const answer = await fetch(relayed(original.download.url), { signal });
|
|
10
16
|
if (!answer.ok)
|
|
11
17
|
throw new Error(`${file.name} did not download: ${answer.status}`);
|
|
@@ -16,7 +22,7 @@ export async function placeFiles(house, directory, files, signal) {
|
|
|
16
22
|
});
|
|
17
23
|
await mkdir(folder, { recursive: true });
|
|
18
24
|
const path = join(folder, file.name);
|
|
19
|
-
await writeFile(path, Buffer.from(await answer.arrayBuffer()));
|
|
25
|
+
await writeFile(path, verified(Buffer.from(await answer.arrayBuffer()), file.sha256, file.name));
|
|
20
26
|
paths.push(path);
|
|
21
27
|
}
|
|
22
28
|
return paths;
|
package/dist/git.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { isUtf8 } from 'node:buffer';
|
|
2
3
|
import { createHash } from 'node:crypto';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
import { withoutProxy } from './clis.js';
|
|
@@ -48,14 +49,15 @@ export async function tree(repository, commit) {
|
|
|
48
49
|
}
|
|
49
50
|
return entries;
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
async function blob(repository, object) {
|
|
53
|
+
return (await run(repository, ['cat-file', 'blob', object])).stdout;
|
|
54
|
+
}
|
|
55
|
+
export async function blobText(repository, object) {
|
|
56
|
+
return (await blob(repository, object)).toString('utf8');
|
|
57
|
+
}
|
|
58
|
+
export async function isText(repository, object, maxBytes) {
|
|
59
|
+
const bytes = await blob(repository, object);
|
|
60
|
+
return bytes.length <= maxBytes && isUtf8(bytes) && !bytes.includes(0);
|
|
59
61
|
}
|
|
60
62
|
export function blobId(content) {
|
|
61
63
|
const bytes = Buffer.from(content, 'utf8');
|
package/dist/home.js
CHANGED
|
@@ -1,13 +1,63 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { accessSync, chmodSync, constants, existsSync, linkSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync } from 'node:fs';
|
|
2
3
|
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { homedir, tmpdir } from 'node:os';
|
|
5
|
+
import { join, resolve } from 'node:path';
|
|
6
|
+
import { selectRelay } from './relay.js';
|
|
5
7
|
const ENROLMENT = 'credential.json';
|
|
6
8
|
const DISCONNECTED = 'disconnected.json';
|
|
7
9
|
const CONFIGURATION = 'kit.json';
|
|
8
10
|
const OWN_AGENT = 'own-agent.json';
|
|
11
|
+
const RESTART = 'restart';
|
|
12
|
+
let defaulted;
|
|
13
|
+
function writable(directory) {
|
|
14
|
+
try {
|
|
15
|
+
accessSync(directory, constants.W_OK | constants.X_OK);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function privateDirectory(path) {
|
|
23
|
+
try {
|
|
24
|
+
mkdirSync(path, { mode: 0o700 });
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (error.code !== 'EEXIST')
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
const found = lstatSync(path);
|
|
31
|
+
if (found.isSymbolicLink())
|
|
32
|
+
throw new Error(`${path} is a symbolic link`);
|
|
33
|
+
if (!found.isDirectory())
|
|
34
|
+
throw new Error(`${path} is not a directory`);
|
|
35
|
+
if (found.uid !== process.geteuid())
|
|
36
|
+
throw new Error(`${path} is not owned by this User`);
|
|
37
|
+
chmodSync(path, 0o700);
|
|
38
|
+
return path;
|
|
39
|
+
}
|
|
40
|
+
function ownTemporaryHome() {
|
|
41
|
+
return privateDirectory(join(tmpdir(), `house-kit-${process.getuid()}`));
|
|
42
|
+
}
|
|
43
|
+
function defaultHome() {
|
|
44
|
+
const home = join(homedir(), '.house-kit');
|
|
45
|
+
return existsSync(home) || writable(homedir()) ? home : ownTemporaryHome();
|
|
46
|
+
}
|
|
9
47
|
export function kitHome() {
|
|
10
|
-
return process.env.HOUSE_KIT_HOME
|
|
48
|
+
return process.env.HOUSE_KIT_HOME || (defaulted ??= defaultHome());
|
|
49
|
+
}
|
|
50
|
+
export function socketDirectory() {
|
|
51
|
+
const sockets = privateDirectory(join(realpathSync('/tmp'), `hk-${process.geteuid()}`));
|
|
52
|
+
const home = kitHome();
|
|
53
|
+
const physical = existsSync(home) ? realpathSync(home) : resolve(home);
|
|
54
|
+
return privateDirectory(join(sockets, createHash('sha256').update(physical).digest('hex').slice(0, 12)));
|
|
55
|
+
}
|
|
56
|
+
export function ownerSocket() {
|
|
57
|
+
return join(socketDirectory(), 'owner.sock');
|
|
58
|
+
}
|
|
59
|
+
export function ownerLock() {
|
|
60
|
+
return join(socketDirectory(), 'owner.lock');
|
|
11
61
|
}
|
|
12
62
|
export function workspaceRoot() {
|
|
13
63
|
return process.env.HOUSE_KIT_WORKSPACE ?? '/agents/house';
|
|
@@ -28,12 +78,15 @@ export async function readHome(name) {
|
|
|
28
78
|
export async function writeHome(name, value) {
|
|
29
79
|
const home = kitHome();
|
|
30
80
|
await mkdir(home, { recursive: true, mode: 0o700 });
|
|
31
|
-
const temporary = join(home, `${name}.${
|
|
81
|
+
const temporary = join(home, `${name}.${randomUUID()}`);
|
|
32
82
|
await writeFile(temporary, `${JSON.stringify(value)}\n`, { mode: 0o600 });
|
|
33
83
|
await rename(temporary, join(home, name));
|
|
34
84
|
}
|
|
35
|
-
export function readEnrolment() {
|
|
36
|
-
|
|
85
|
+
export async function readEnrolment() {
|
|
86
|
+
const enrolment = await readHome(ENROLMENT);
|
|
87
|
+
if (enrolment !== null)
|
|
88
|
+
selectRelay(process.env.HOUSE_KIT_RELAY || enrolment.relay);
|
|
89
|
+
return enrolment;
|
|
37
90
|
}
|
|
38
91
|
export async function writeEnrolment(enrolment) {
|
|
39
92
|
await writeHome(ENROLMENT, enrolment);
|
|
@@ -42,13 +95,49 @@ export async function writeEnrolment(enrolment) {
|
|
|
42
95
|
export function readDisconnected() {
|
|
43
96
|
return readHome(DISCONNECTED);
|
|
44
97
|
}
|
|
98
|
+
export function forgetDisconnected() {
|
|
99
|
+
return rm(join(kitHome(), DISCONNECTED), { force: true });
|
|
100
|
+
}
|
|
45
101
|
export async function forgetEnrolment({ house, environment }) {
|
|
46
102
|
await writeHome(DISCONNECTED, { house, environment });
|
|
47
103
|
await rm(join(kitHome(), ENROLMENT), { force: true });
|
|
48
104
|
}
|
|
105
|
+
export async function forgetRejected(rejected) {
|
|
106
|
+
const held = await readHome(ENROLMENT);
|
|
107
|
+
if (held?.credential !== rejected)
|
|
108
|
+
return;
|
|
109
|
+
await writeHome(DISCONNECTED, { house: held.house, environment: held.environment });
|
|
110
|
+
const enrolment = join(kitHome(), ENROLMENT);
|
|
111
|
+
const taken = `${enrolment}.${process.pid}`;
|
|
112
|
+
try {
|
|
113
|
+
renameSync(enrolment, taken);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (error.code === 'ENOENT')
|
|
117
|
+
return;
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
if (JSON.parse(readFileSync(taken, 'utf8')).credential !== rejected) {
|
|
121
|
+
try {
|
|
122
|
+
linkSync(taken, enrolment);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
if (error.code !== 'EEXIST')
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
rmSync(join(kitHome(), DISCONNECTED), { force: true });
|
|
129
|
+
}
|
|
130
|
+
rmSync(taken);
|
|
131
|
+
}
|
|
49
132
|
export function enrolled() {
|
|
50
133
|
return existsSync(join(kitHome(), ENROLMENT));
|
|
51
134
|
}
|
|
135
|
+
export function restartAsked() {
|
|
136
|
+
return existsSync(join(kitHome(), RESTART));
|
|
137
|
+
}
|
|
138
|
+
export function forgetRestart() {
|
|
139
|
+
return rm(join(kitHome(), RESTART), { force: true });
|
|
140
|
+
}
|
|
52
141
|
export function readOwnAgent() {
|
|
53
142
|
return readHome(OWN_AGENT);
|
|
54
143
|
}
|