@agentshouse/kit 0.2.1-c441d29eb63f4 → 0.2.1-c4613862fad84
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 +14 -14
- package/dist/acp.js +57 -6
- package/dist/agents.js +54 -31
- package/dist/authority-main.js +2 -2
- package/dist/bridge.js +1 -1
- package/dist/building.js +50 -0
- package/dist/calls.js +11 -3
- package/dist/clis.js +28 -37
- package/dist/connection.js +9 -30
- package/dist/conversations.js +149 -45
- package/dist/end.js +8 -2
- package/dist/events.js +29 -8
- package/dist/files.js +33 -17
- package/dist/git.js +2 -2
- package/dist/home.js +0 -7
- package/dist/house-cli.js +24 -11
- package/dist/house-main.js +3 -3
- package/dist/instructions.js +4 -4
- package/dist/kit-cli.js +2 -2
- package/dist/kit-main.js +3 -3
- package/dist/login.js +3 -1
- package/dist/mcp.js +2 -0
- package/dist/placement.js +10 -18
- package/dist/relay.js +21 -0
- package/dist/resident.js +19 -15
- package/dist/scope.js +12 -4
- package/dist/scripts.js +255 -0
- package/dist/sign-in.js +3 -2
- package/dist/silence.js +28 -0
- package/dist/skills.js +38 -12
- package/dist/transcription.js +32 -23
- package/dist/update.js +109 -120
- package/dist/wire.js +23 -5
- package/package.json +16 -17
- package/dist/bootstrap.json +0 -24
- package/dist/clis-main.js +0 -23
- package/dist/web-wizard.js +0 -100
package/dist/files.js
CHANGED
|
@@ -9,6 +9,7 @@ import { retryDelay } from './api.js';
|
|
|
9
9
|
import { Unsettled } from './mcp.js';
|
|
10
10
|
import { relayed } from './relay.js';
|
|
11
11
|
const STALLED_ROUNDS = 8;
|
|
12
|
+
const PART_CHECKSUM = 'x-amz-checksum-sha256';
|
|
12
13
|
const LINK = /\]\((\/rooms\/[^/)\s]+\/[^)\s]+)\)/gu;
|
|
13
14
|
export function linkedPaths(text) {
|
|
14
15
|
return [...new Set(Array.from(text.matchAll(LINK), (link) => link[1]))];
|
|
@@ -32,17 +33,39 @@ function paused(milliseconds) {
|
|
|
32
33
|
export function storeRefusal(act, status) {
|
|
33
34
|
return new Error(`the ${act} answered ${status}; ${act} the file again`);
|
|
34
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
|
+
}
|
|
35
52
|
export async function uploaded(declared, uploading) {
|
|
36
|
-
|
|
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);
|
|
37
57
|
let idle = 0;
|
|
38
58
|
let stalled = 0;
|
|
39
|
-
|
|
59
|
+
while ('upload' in answer) {
|
|
60
|
+
const { id, parts } = answer.upload;
|
|
40
61
|
let sent = 0;
|
|
41
62
|
let failure = null;
|
|
42
|
-
for (const { part, url } of
|
|
43
|
-
const start = (part - 1) *
|
|
63
|
+
for (const { part, url } of parts ?? []) {
|
|
64
|
+
const start = (part - 1) * partBytes;
|
|
44
65
|
try {
|
|
45
|
-
await uploading.send(url, start, Math.min(start +
|
|
66
|
+
await uploading.send(url, start, Math.min(start + partBytes, uploading.bytes), {
|
|
67
|
+
[PART_CHECKSUM]: digests[part - 1].toString('base64'),
|
|
68
|
+
});
|
|
46
69
|
sent++;
|
|
47
70
|
}
|
|
48
71
|
catch (error) {
|
|
@@ -57,24 +80,17 @@ export async function uploaded(declared, uploading) {
|
|
|
57
80
|
else {
|
|
58
81
|
if (failure !== null && ++stalled >= STALLED_ROUNDS)
|
|
59
82
|
throw failure;
|
|
60
|
-
// A round that sent no part, its store unreachable
|
|
83
|
+
// A round that sent no part, its store unreachable, is followed by the next finishing call on the jittered curve.
|
|
61
84
|
await paused(retryDelay(idle++));
|
|
62
85
|
}
|
|
63
|
-
|
|
64
|
-
while ('checking' in answer) {
|
|
65
|
-
// A finishing call that finds House still checking the stored bytes is sent again after five seconds, the cadence at which House takes up a check it handed over.
|
|
66
|
-
await paused(5_000);
|
|
67
|
-
answer = await finishing(uploading, upload.id);
|
|
68
|
-
}
|
|
69
|
-
if (!('upload' in answer))
|
|
70
|
-
return answer;
|
|
71
|
-
upload = answer.upload;
|
|
86
|
+
answer = await finishing(uploading, id, partSha256);
|
|
72
87
|
}
|
|
88
|
+
return answer;
|
|
73
89
|
}
|
|
74
|
-
async function finishing(uploading, upload) {
|
|
90
|
+
async function finishing(uploading, upload, partSha256) {
|
|
75
91
|
for (let unsettled = 1;; unsettled++) {
|
|
76
92
|
try {
|
|
77
|
-
return await uploading.finish(upload);
|
|
93
|
+
return await uploading.finish(upload, partSha256);
|
|
78
94
|
}
|
|
79
95
|
catch (error) {
|
|
80
96
|
if (!(error instanceof Unsettled) || unsettled === STALLED_ROUNDS)
|
package/dist/git.js
CHANGED
|
@@ -2,8 +2,8 @@ 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
|
}
|
|
@@ -11,7 +11,7 @@ export const HOUSE_REF = 'refs/house/received';
|
|
|
11
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));
|
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
|
}
|
package/dist/house-cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { createReadStream } from 'node:fs';
|
|
3
|
-
import { request
|
|
3
|
+
import { request } from 'node:http';
|
|
4
4
|
import { request as httpsRequest } from 'node:https';
|
|
5
5
|
import { extname, join, resolve } from 'node:path';
|
|
6
6
|
import { Readable } from 'node:stream';
|
|
@@ -10,7 +10,7 @@ import { HOUSE_REF } from './git.js';
|
|
|
10
10
|
import { ownerSocket, readOwnAgent } from './home.js';
|
|
11
11
|
import { callHouse, rpc, UNREACHABLE, Unsettled } from './mcp.js';
|
|
12
12
|
import { refusalOf, retryable } from './refusals.js';
|
|
13
|
-
import { relayed, selectRelay } from './relay.js';
|
|
13
|
+
import { proxyUnlessRelayed, relayed, selectRelay } from './relay.js';
|
|
14
14
|
import { parse } from 'yaml';
|
|
15
15
|
const USAGE = "usage: house <tool> ['<arguments as JSON>']";
|
|
16
16
|
const PUSH_USAGE = 'usage: house push [commit] [--owner]';
|
|
@@ -89,7 +89,6 @@ async function direct(message) {
|
|
|
89
89
|
if (ownAgent === null)
|
|
90
90
|
throw new Error(UNCONNECTED);
|
|
91
91
|
selectRelay(process.env.HOUSE_KIT_RELAY || ownAgent.relay);
|
|
92
|
-
setGlobalProxyFromEnv();
|
|
93
92
|
const answer = await callHouse(ownAgent.house, ownAgent.credential, message).catch(() => {
|
|
94
93
|
throw new Unsettled(UNREACHABLE);
|
|
95
94
|
});
|
|
@@ -120,7 +119,7 @@ function opened(url, method, headers, body) {
|
|
|
120
119
|
const local = socketPath !== undefined;
|
|
121
120
|
const target = local ? null : relayed(url);
|
|
122
121
|
if (!local)
|
|
123
|
-
|
|
122
|
+
proxyUnlessRelayed();
|
|
124
123
|
const sent = target === null
|
|
125
124
|
? request({ socketPath, path: url, method, headers }, answered)
|
|
126
125
|
: (target.protocol === 'https:' ? httpsRequest : request)(target, { method, headers }, answered);
|
|
@@ -131,8 +130,19 @@ function opened(url, method, headers, body) {
|
|
|
131
130
|
pipeline(body, sent).catch(() => undefined);
|
|
132
131
|
});
|
|
133
132
|
}
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
function ranged(file, start, end) {
|
|
134
|
+
return end === start ? undefined : createReadStream(file, { start, end: end - 1 });
|
|
135
|
+
}
|
|
136
|
+
async function* read(file, start, end) {
|
|
137
|
+
try {
|
|
138
|
+
yield* ranged(file, start, end) ?? [];
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
throw unreadable(file, error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function sentPart(url, file, start, end, headers) {
|
|
145
|
+
const answer = await opened(url, 'PUT', { ...headers, 'content-length': String(end - start) }, ranged(file, start, end));
|
|
136
146
|
await answer.toArray();
|
|
137
147
|
if (answer.statusCode !== 200)
|
|
138
148
|
throw storeRefusal('upload', answer.statusCode);
|
|
@@ -171,10 +181,10 @@ async function described(file) {
|
|
|
171
181
|
const mediaType = MEDIA_TYPES.get(extname(file).slice(1).toLowerCase());
|
|
172
182
|
return { bytes, sha256: digest.digest('hex'), ...(mediaType === undefined ? {} : { media_type: mediaType }) };
|
|
173
183
|
}
|
|
174
|
-
async function finished(upload, room) {
|
|
175
|
-
const text = await called('upload', { upload, room_ref: room });
|
|
184
|
+
async function finished(upload, room, partSha256) {
|
|
185
|
+
const text = await called('upload', { upload, room_ref: room, part_sha256: partSha256 });
|
|
176
186
|
const answer = parse(text);
|
|
177
|
-
return answer !== null &&
|
|
187
|
+
return answer !== null && 'upload' in answer ? answer : { text };
|
|
178
188
|
}
|
|
179
189
|
async function upload({ file, ...destination }) {
|
|
180
190
|
if (typeof file !== 'string' || file === '')
|
|
@@ -185,9 +195,12 @@ async function upload({ file, ...destination }) {
|
|
|
185
195
|
if (declared?.upload === undefined)
|
|
186
196
|
return answer;
|
|
187
197
|
const accepted = await uploaded(declared.upload, {
|
|
198
|
+
name: file,
|
|
188
199
|
bytes: description.bytes,
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
sha256: description.sha256,
|
|
201
|
+
read: (start, end) => read(file, start, end),
|
|
202
|
+
send: (url, start, end, headers) => sentPart(url, file, start, end, headers),
|
|
203
|
+
finish: (id, partSha256) => finished(id, destination.room_ref, partSha256),
|
|
191
204
|
});
|
|
192
205
|
return accepted.text;
|
|
193
206
|
}
|
package/dist/house-main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const [major = 0, minor = 0] = process.versions.node.split('.').map(Number);
|
|
3
|
-
if (major < 24 || (major === 24 && minor <
|
|
4
|
-
process.stderr.write(`house: the Kit needs Node 24.
|
|
2
|
+
const [major = 0, minor = 0, patch = 0] = process.versions.node.split('.').map(Number);
|
|
3
|
+
if (major < 24 || (major === 24 && (minor < 22 || (minor === 22 && patch < 1)))) {
|
|
4
|
+
process.stderr.write(`house: the Kit needs Node 24.22.1 or newer; this is ${process.versions.node}\n`);
|
|
5
5
|
process.exitCode = 1;
|
|
6
6
|
}
|
|
7
7
|
else {
|
package/dist/instructions.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { agentInstructions } from '@agentshouse/skills';
|
|
2
2
|
import { HOUSE_REF } from './git.js';
|
|
3
|
-
const
|
|
3
|
+
const OWNER_RULES = '/rooms/private/owner-rules.md';
|
|
4
4
|
const UNREADABLE = /^(?:\S+: )?(?:path_not_found|room_not_found|operation_denied)\b/;
|
|
5
5
|
const FAILED = /^exit: [1-9]\d*$/;
|
|
6
6
|
const CUT = /^stderr: shell: output_cut (\d+) of \d+ bytes; continue with: (.+)$/;
|
|
@@ -8,9 +8,9 @@ function cutOf(lines) {
|
|
|
8
8
|
const cut = CUT.exec(lines[0] ?? '');
|
|
9
9
|
return cut !== null && Buffer.byteLength(lines.slice(1).join('\n')) === Number(cut[1]) ? cut : null;
|
|
10
10
|
}
|
|
11
|
-
async function
|
|
11
|
+
async function readOwnerRules(bridge) {
|
|
12
12
|
let document = '';
|
|
13
|
-
for (let command = `cat ${
|
|
13
|
+
for (let command = `cat ${OWNER_RULES}`; command !== undefined;) {
|
|
14
14
|
const read = await bridge.tool('shell', { command });
|
|
15
15
|
const text = read.content.map((content) => content.text).join('\n');
|
|
16
16
|
const lines = text.split('\n');
|
|
@@ -28,5 +28,5 @@ async function readHowWeWork(bridge) {
|
|
|
28
28
|
}
|
|
29
29
|
export async function instructions(bridge, base, copies) {
|
|
30
30
|
const lines = agentInstructions(HOUSE_REF, copies);
|
|
31
|
-
return [lines.join('\n'), base, (await
|
|
31
|
+
return [lines.join('\n'), base, (await readOwnerRules(bridge)).replace(/\n$/, '')].filter((part) => part !== '').join('\n\n');
|
|
32
32
|
}
|
package/dist/kit-cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { setGlobalProxyFromEnv } from 'node:http';
|
|
2
1
|
import { parseArgs } from 'node:util';
|
|
3
2
|
import { answerGitCredential } from './app-git.js';
|
|
4
3
|
import { chatgpt } from './chatgpt.js';
|
|
5
4
|
import { login } from './login.js';
|
|
6
5
|
import { logout } from './logout.js';
|
|
6
|
+
import { proxyUnlessRelayed } from './relay.js';
|
|
7
7
|
import { resident } from './resident.js';
|
|
8
8
|
const USAGE = 'usage: kit <login|logout|resident|chatgpt>\n';
|
|
9
9
|
const COMMANDS = new Map([
|
|
@@ -36,7 +36,7 @@ const COMMANDS = new Map([
|
|
|
36
36
|
],
|
|
37
37
|
]);
|
|
38
38
|
const [command, ...argv] = process.argv.slice(2);
|
|
39
|
-
|
|
39
|
+
proxyUnlessRelayed();
|
|
40
40
|
async function run() {
|
|
41
41
|
if (command === 'login')
|
|
42
42
|
return login(argv);
|
package/dist/kit-main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const [major = 0, minor = 0] = process.versions.node.split('.').map(Number);
|
|
3
|
-
if (major < 24 || (major === 24 && minor <
|
|
4
|
-
process.stderr.write(`kit: the Kit needs Node 24.
|
|
2
|
+
const [major = 0, minor = 0, patch = 0] = process.versions.node.split('.').map(Number);
|
|
3
|
+
if (major < 24 || (major === 24 && (minor < 22 || (minor === 22 && patch < 1)))) {
|
|
4
|
+
process.stderr.write(`kit: the Kit needs Node 24.22.1 or newer; this is ${process.versions.node}\n`);
|
|
5
5
|
process.exitCode = 1;
|
|
6
6
|
}
|
|
7
7
|
else {
|
package/dist/login.js
CHANGED
|
@@ -10,6 +10,7 @@ import { refusalOf } from './refusals.js';
|
|
|
10
10
|
import { selectRelay } from './relay.js';
|
|
11
11
|
import { startService } from './service.js';
|
|
12
12
|
import { placeSkillSet } from './skills.js';
|
|
13
|
+
import { endUnserved } from './wire.js';
|
|
13
14
|
export const DEFAULT_HOUSE = 'https://agents.house';
|
|
14
15
|
const OPENER = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
15
16
|
// Polling every two seconds brings the User's confirmation to the waiting Kit moments after they give it.
|
|
@@ -25,6 +26,7 @@ export async function accepted(house, credential) {
|
|
|
25
26
|
return true;
|
|
26
27
|
if (checked.status === 401)
|
|
27
28
|
return false;
|
|
29
|
+
endUnserved(checked.text);
|
|
28
30
|
throw new Error(`House answered ${checked.status} when Kit checked this computer's own agent's connection: ${checked.text}`);
|
|
29
31
|
}
|
|
30
32
|
const USAGE = `usage: kit login [--house <origin>] [--environment <id>] [--rooms <ref>[,<ref>...] | --no-private]
|
|
@@ -119,7 +121,7 @@ export async function login(argv) {
|
|
|
119
121
|
open(started.start_url);
|
|
120
122
|
exchanged = await retrying(() => post(house, '/kit/token', { login: started.login, code_verifier: verifier }), undefined, () => POLL_INTERVAL_MS);
|
|
121
123
|
}
|
|
122
|
-
await placeSkillSet();
|
|
124
|
+
await placeSkillSet(house);
|
|
123
125
|
await writeCodexRule().catch((error) => process.stderr.write(`kit: ${error.message}\n`));
|
|
124
126
|
const relayRecord = relay === undefined ? {} : { relay };
|
|
125
127
|
await writeEnrolment({
|
package/dist/mcp.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { KIT_RELEASE } from './clis.js';
|
|
3
3
|
import { relayed } from './relay.js';
|
|
4
|
+
import { KIT_WIRE, KIT_WIRE_HEADER } from './wire.js';
|
|
4
5
|
const MCP_PROTOCOL_VERSION = '2026-07-28';
|
|
5
6
|
export const UNREACHABLE = 'House is unreachable; call again in a minute';
|
|
6
7
|
export class Unsettled extends Error {
|
|
@@ -33,6 +34,7 @@ export async function callHouse(origin, credential, message, operation = null, s
|
|
|
33
34
|
'mcp-protocol-version': MCP_PROTOCOL_VERSION,
|
|
34
35
|
'mcp-method': message.method,
|
|
35
36
|
...(tool === null ? {} : { 'mcp-name': tool }),
|
|
37
|
+
[KIT_WIRE_HEADER]: String(KIT_WIRE),
|
|
36
38
|
},
|
|
37
39
|
body: mcpBody(message, operation),
|
|
38
40
|
signal,
|
package/dist/placement.js
CHANGED
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { run } from './clis.js';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
4
3
|
import { kitHome } from './home.js';
|
|
5
|
-
export function service() {
|
|
6
|
-
if (process.platform !== 'linux' || !process.env.INVOCATION_ID)
|
|
7
|
-
return null;
|
|
8
|
-
return process.env.MANAGERPID ? 'user' : 'system';
|
|
9
|
-
}
|
|
10
|
-
export function throughSudo() {
|
|
11
|
-
return service() === 'system' && process.getuid() !== 0;
|
|
12
|
-
}
|
|
13
|
-
export async function updatesItself() {
|
|
14
|
-
if (service() === null)
|
|
15
|
-
return false;
|
|
16
|
-
if (!throughSudo())
|
|
17
|
-
return true;
|
|
18
|
-
// sudo answers at once, so ten seconds only bounds a stuck sudo.
|
|
19
|
-
return (await run('sudo', ['-n', 'true'], 10_000)).status === 0;
|
|
20
|
-
}
|
|
21
4
|
function recorded(name) {
|
|
22
5
|
try {
|
|
23
6
|
return readFileSync(join(kitHome(), name), 'utf8').trim();
|
|
@@ -37,3 +20,12 @@ export function machine() {
|
|
|
37
20
|
return placed;
|
|
38
21
|
return process.platform === 'darwin' ? 'macos' : 'linux';
|
|
39
22
|
}
|
|
23
|
+
export function placedPrefix() {
|
|
24
|
+
const prefix = process.env.HOUSE_KIT_PREFIX;
|
|
25
|
+
if (prefix === undefined || prefix === '')
|
|
26
|
+
throw new Error('this Kit process was not started by a launcher its installer placed');
|
|
27
|
+
return resolve(prefix);
|
|
28
|
+
}
|
|
29
|
+
export function releaseDirectory(release) {
|
|
30
|
+
return join(placedPrefix(), 'releases', release);
|
|
31
|
+
}
|
package/dist/relay.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { setGlobalProxyFromEnv } from 'node:http';
|
|
2
|
+
const PROXY = /^https?_proxy$/i;
|
|
3
|
+
const LOOPBACK = ['127.0.0.1', 'localhost', '::1'];
|
|
1
4
|
let selected;
|
|
5
|
+
let restoreProxy;
|
|
2
6
|
export function selectRelay(relay) {
|
|
3
7
|
selected = relay === undefined || relay === '' ? null : relay;
|
|
8
|
+
proxyUnlessRelayed();
|
|
4
9
|
}
|
|
5
10
|
export function relayOrigin() {
|
|
6
11
|
if (selected !== undefined)
|
|
@@ -8,6 +13,22 @@ export function relayOrigin() {
|
|
|
8
13
|
const ambient = process.env.HOUSE_KIT_RELAY;
|
|
9
14
|
return ambient === undefined || ambient === '' ? undefined : ambient;
|
|
10
15
|
}
|
|
16
|
+
function unlessRelayed() {
|
|
17
|
+
if (relayOrigin() === undefined)
|
|
18
|
+
return { ...process.env };
|
|
19
|
+
return Object.fromEntries(Object.entries(process.env).filter(([name]) => !PROXY.test(name)));
|
|
20
|
+
}
|
|
21
|
+
export function proxyUnlessRelayed() {
|
|
22
|
+
restoreProxy?.();
|
|
23
|
+
restoreProxy = setGlobalProxyFromEnv(unlessRelayed());
|
|
24
|
+
}
|
|
25
|
+
export function childEnvironment() {
|
|
26
|
+
const environment = unlessRelayed();
|
|
27
|
+
if (!Object.keys(environment).some((name) => PROXY.test(name)))
|
|
28
|
+
return environment;
|
|
29
|
+
const bypassed = [environment.NO_PROXY ?? environment.no_proxy, ...LOOPBACK].filter((entry) => entry !== undefined && entry !== '').join(',');
|
|
30
|
+
return { ...environment, NO_PROXY: bypassed, no_proxy: bypassed };
|
|
31
|
+
}
|
|
11
32
|
export function relayed(target) {
|
|
12
33
|
const url = new URL(target);
|
|
13
34
|
const relay = relayOrigin();
|
package/dist/resident.js
CHANGED
|
@@ -10,16 +10,17 @@ import { house } from './api.js';
|
|
|
10
10
|
import { received } from './bridge.js';
|
|
11
11
|
import { CONVERSATION_METHODS, Conversations } from './conversations.js';
|
|
12
12
|
import { LocalCopies } from './copies.js';
|
|
13
|
-
import { restart } from './end.js';
|
|
14
|
-
import { agentBase, enrolled,
|
|
13
|
+
import { restart, unknownMethod } from './end.js';
|
|
14
|
+
import { agentBase, enrolled, kitHome, ownerLock, ownerSocket, readEnrolment } from './home.js';
|
|
15
15
|
import { Calls } from './calls.js';
|
|
16
16
|
import { killDescendants } from './scope.js';
|
|
17
|
+
import { removeInputs, Scripts } from './scripts.js';
|
|
17
18
|
import { quoted } from './shell.js';
|
|
18
19
|
import { SignIns } from './sign-in.js';
|
|
19
20
|
import { placeSkillSet } from './skills.js';
|
|
20
21
|
import { holdConnection } from './connection.js';
|
|
21
22
|
import { KitUpdate } from './update.js';
|
|
22
|
-
import {
|
|
23
|
+
import { takeUnservedRelease } from './wire.js';
|
|
23
24
|
import { codexLauncher } from './work.js';
|
|
24
25
|
function report(error) {
|
|
25
26
|
process.stderr.write(`kit: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -110,14 +111,13 @@ function connected() {
|
|
|
110
111
|
}
|
|
111
112
|
async function started() {
|
|
112
113
|
await connected();
|
|
113
|
-
await forgetRestart();
|
|
114
114
|
watch(kitHome(), () => {
|
|
115
115
|
if (enrolled())
|
|
116
116
|
return;
|
|
117
117
|
killDescendants();
|
|
118
118
|
process.exit(1);
|
|
119
119
|
});
|
|
120
|
-
await placeSkillSet();
|
|
120
|
+
await placeSkillSet((await readEnrolment()).house);
|
|
121
121
|
await mkdir(agentBase(), { recursive: true });
|
|
122
122
|
await installShim(join(kitHome(), 'shim', 'git'), 'git-main');
|
|
123
123
|
await installShim(codexLauncher(), 'codex-main');
|
|
@@ -140,8 +140,8 @@ export async function resident() {
|
|
|
140
140
|
if (!idle()) {
|
|
141
141
|
reported = false;
|
|
142
142
|
}
|
|
143
|
-
else if (
|
|
144
|
-
restart(
|
|
143
|
+
else if (update.placedRelease() !== null && conversations.quiet()) {
|
|
144
|
+
restart(`Kit ${update.placedRelease()} is placed; its supervisor starts it`);
|
|
145
145
|
}
|
|
146
146
|
else if (!reported) {
|
|
147
147
|
reported = true;
|
|
@@ -159,34 +159,38 @@ export async function resident() {
|
|
|
159
159
|
});
|
|
160
160
|
const signIns = new SignIns(house, agents, calls);
|
|
161
161
|
const update = new KitUpdate(calls);
|
|
162
|
-
|
|
162
|
+
if (process.env.HOUSE_KIT_SANDBOX !== '1')
|
|
163
|
+
takeUnservedRelease((release) => logged(update.takeRefused(release)));
|
|
164
|
+
removeInputs();
|
|
165
|
+
const scripts = new Scripts(calls, (notification) => connection?.send(notification) ?? false);
|
|
163
166
|
for (const signal of ['SIGTERM', 'SIGINT']) {
|
|
164
167
|
process.once(signal, () => {
|
|
165
168
|
killDescendants();
|
|
169
|
+
removeInputs();
|
|
166
170
|
process.kill(process.pid, signal);
|
|
167
171
|
});
|
|
168
172
|
}
|
|
169
173
|
await house.deliver('/kit/restarted', {}).catch(report);
|
|
170
|
-
await update.resumed().catch(report);
|
|
171
174
|
logged(agents.refresh());
|
|
175
|
+
agents.keepCurrent();
|
|
172
176
|
connection = holdConnection({
|
|
173
177
|
opened: () => {
|
|
174
178
|
conversations.opened();
|
|
175
179
|
changed();
|
|
176
180
|
logged(copies.connected());
|
|
177
181
|
},
|
|
178
|
-
closed: () =>
|
|
182
|
+
closed: () => scripts.disconnected(),
|
|
179
183
|
call: (call) => {
|
|
180
184
|
if (call.kind === 'sign_in')
|
|
181
185
|
signIns.start(call);
|
|
182
186
|
else if (call.kind === 'update')
|
|
183
|
-
update.
|
|
184
|
-
else if (call.kind === 'run_wizard')
|
|
185
|
-
|
|
187
|
+
update.take(call);
|
|
188
|
+
else if (call.kind === 'run_wizard' || call.kind === 'run_function')
|
|
189
|
+
scripts.start(call);
|
|
186
190
|
else if (CONVERSATION_METHODS.has(call.kind))
|
|
187
191
|
conversations.call(call);
|
|
188
192
|
else {
|
|
189
|
-
const refused = { refused:
|
|
193
|
+
const refused = { refused: unknownMethod(call.kind) };
|
|
190
194
|
void calls.carry(call.input_id, async () => ({ result: refused }));
|
|
191
195
|
}
|
|
192
196
|
},
|
|
@@ -197,7 +201,7 @@ export async function resident() {
|
|
|
197
201
|
logged(copies.changed(notification.rooms));
|
|
198
202
|
if (notification.type === 'work_available' && notification.subject === 'agents')
|
|
199
203
|
logged(agents.refresh());
|
|
200
|
-
|
|
204
|
+
scripts.notified(notification);
|
|
201
205
|
},
|
|
202
206
|
});
|
|
203
207
|
}
|
package/dist/scope.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { mkdirSync, readdirSync, readFileSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { mkdirSync, readdirSync, readFileSync, realpathSync, rmdirSync, statSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
const LISTING_BYTES = 1 << 28;
|
|
5
5
|
const CGROUPS = '/sys/fs/cgroup';
|
|
@@ -122,15 +122,23 @@ function members(group) {
|
|
|
122
122
|
}
|
|
123
123
|
return pids;
|
|
124
124
|
}
|
|
125
|
+
function besideCli(cli) {
|
|
126
|
+
try {
|
|
127
|
+
return `${realpathSync(cli)}-`;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
125
133
|
export class Scope {
|
|
126
134
|
marker;
|
|
127
|
-
|
|
135
|
+
helpers;
|
|
128
136
|
group;
|
|
129
137
|
session = null;
|
|
130
138
|
recorded = null;
|
|
131
139
|
constructor(marker, name, cli) {
|
|
132
140
|
this.marker = marker;
|
|
133
|
-
this.
|
|
141
|
+
this.helpers = besideCli(cli);
|
|
134
142
|
const own = ownGroup();
|
|
135
143
|
this.group = own === null ? null : join(own, name);
|
|
136
144
|
try {
|
|
@@ -168,7 +176,7 @@ export class Scope {
|
|
|
168
176
|
return true;
|
|
169
177
|
if (this.recorded === null)
|
|
170
178
|
return found.size > 0;
|
|
171
|
-
return [...found].some(([pid, { session, command }]) => session !== this.session || !(this.recorded.has(pid) || command.startsWith(
|
|
179
|
+
return [...found].some(([pid, { session, command }]) => session !== this.session || !(this.recorded.has(pid) || (this.helpers !== null && command.startsWith(this.helpers))));
|
|
172
180
|
}
|
|
173
181
|
empty() {
|
|
174
182
|
return this.found()?.size === 0;
|