@agentshouse/kit 0.2.1-ca1f38641026d → 0.2.1-cab5821cf0a77
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 +10 -10
- package/dist/agents.js +42 -10
- package/dist/building.js +7 -3
- package/dist/clis.js +16 -49
- package/dist/connection.js +2 -13
- package/dist/end.js +1 -1
- package/dist/events.js +2 -2
- package/dist/home.js +0 -7
- package/dist/placement.js +10 -18
- package/dist/resident.js +9 -10
- package/dist/scope.js +12 -4
- package/dist/update.js +105 -159
- package/dist/wire.js +23 -5
- package/package.json +2 -2
- package/dist/clis-main.js +0 -23
package/README.md
CHANGED
|
@@ -9,11 +9,11 @@ Run `sh -c 'kit=$(curl -fsSL "$0") || { echo "Kit could not be downloaded; run t
|
|
|
9
9
|
or `& { try { $kit = irm agents.house/kit.ps1 } catch { throw 'Kit could not be downloaded; run this command again.' }; & ([scriptblock]::Create($kit)) @args }` on Windows, which run nothing of a failed download, and again to update; options follow `agents.house/kit`.
|
|
10
10
|
On macOS and Linux it installs Kit natively under your own account, without an admin password, as a service that runs
|
|
11
11
|
while you are logged in. `kit` and `house` are then on your login shell's `PATH`, and your workspace is `~/AgentsHouse` unless you
|
|
12
|
-
pass `--workspace <path>`.
|
|
12
|
+
pass `--workspace <path>`. Every run places the Kit release your Building serves, with the Node.js and agent adapters it runs on, in a directory of its own beside the one that runs, then starts it; the earlier one stays until the next release. Kit also updates itself: when your Building takes another release, Kit places it the same way and, once no turn is open, ends so its service, or the container's restart policy, starts it. When House refuses the Kit that runs as too old or too new, Kit places your Building's release at once, ending any running turn, and ends the same way; while it cannot, it stays offline, prints why with the command that installs it again, and tries again regularly.
|
|
13
13
|
|
|
14
|
-
Add `--container` to run Kit in one Docker container instead, with your
|
|
15
|
-
workspace
|
|
16
|
-
|
|
14
|
+
Add `--container` to run Kit in one Docker container instead, with your workspace mounted at `/agents/house`; your Agents then reach only that
|
|
15
|
+
workspace. The container's image holds no Kit release and no Node.js: every run places them in Kit home's `kit` folder, which a container created again keeps. On Windows,
|
|
16
|
+
Kit home is the Docker volume `house-kit-home` and the container is the one choice. Append a `kit` or `house` command to the container's options to run it in that container.
|
|
17
17
|
Running the command for the other choice on a connected computer asks you to
|
|
18
18
|
confirm, then replaces its Environment, whose Agents end, with a new one. Running it with a different `HOUSE_KIT_HOME`
|
|
19
19
|
from the running Kit process asks you to confirm before it replaces that process, and leaves the earlier Kit home as it was.
|
|
@@ -22,12 +22,12 @@ On your own Linux server, run the Linux command with `--linux` to install Kit na
|
|
|
22
22
|
|
|
23
23
|
Kit is built for macOS 27 on Apple silicon, Ubuntu 26.04 LTS on amd64 and Windows 11 on amd64. On another release, distribution or architecture the command prints one warning line, such as `Warning: macOS 26.7.1 is outdated; update to macOS 27.`, and installs anyway. It refuses only an architecture other than amd64 and arm64, and a native install on a Linux without glibc, where `--container` still runs Kit. On a Linux without systemd it installs and connects Kit but starts nothing: run the `kit resident` command it prints under your own service manager, restarting it whenever it ends with a failure. `--container` installs Docker Engine on Ubuntu and Debian; another Linux needs a working Docker Engine first.
|
|
24
24
|
|
|
25
|
-
Kit runs
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
release installs. Add `--no-skills` to the command to leave them out; running it again without the flag brings them back.
|
|
25
|
+
Kit runs each agent CLI through the link your login shell finds and installs a missing one through that CLI's own
|
|
26
|
+
installer. When it starts and regularly after, Kit runs each CLI's own update command, such as `claude update`, beside
|
|
27
|
+
your conversations: a running conversation keeps its version and the next one runs the new release. A CLI older than
|
|
28
|
+
the oldest release Kit is built for still runs, though its conversations may fail: Kit tells House its version, that
|
|
29
|
+
oldest release and its update command. Kit also gives every Agent on the Environment the agent skills of
|
|
30
|
+
`@agentshouse/skills`, at the version each Kit release installs. Add `--no-skills` to the command to leave them out; running it again without the flag brings them back.
|
|
31
31
|
|
|
32
32
|
## Commands
|
|
33
33
|
|
package/dist/agents.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { client } from '@agentclientprotocol/sdk';
|
|
2
|
-
import { readFile
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
4
|
import { signedOut, startAdapter } from './acp.js';
|
|
5
|
-
import { CLIS, KIT_RELEASE, below,
|
|
5
|
+
import { CLIS, KIT_RELEASE, below, installCli, locate, run, updateCli, versionOf } from './clis.js';
|
|
6
6
|
import { agentBase, kitHome } from './home.js';
|
|
7
|
-
import { updatesItself } from './placement.js';
|
|
8
7
|
import { holdTranscription, transcriptionReport } from './transcription.js';
|
|
9
8
|
import { KIT_WIRE } from './wire.js';
|
|
10
9
|
// Two quiet seconds let a fresh conversation start before background model discovery competes for its CPU.
|
|
11
10
|
const MODEL_PROBE_QUIET_MS = 2_000;
|
|
11
|
+
// A CLI's maker can publish several releases a day, so running each chosen CLI's update command hourly brings a new one to the next conversation within the hour for one small check.
|
|
12
|
+
export const CLI_UPDATE_MS = 60 * 60_000;
|
|
12
13
|
// A sign-in's model read that has not answered in ten seconds leaves its sign-in to the status check, so a hung CLI never holds the sign-in's result.
|
|
13
14
|
const SIGNED_IN_READ_MS = 10_000;
|
|
14
15
|
const SIGNED_OUT = { signed_in: false, models: [], modes: [] };
|
|
@@ -165,13 +166,15 @@ export class Agents {
|
|
|
165
166
|
sending = Promise.resolve();
|
|
166
167
|
sent = null;
|
|
167
168
|
refreshing = 0;
|
|
169
|
+
loaded = Promise.resolve();
|
|
170
|
+
updating = null;
|
|
168
171
|
changed;
|
|
169
172
|
constructor(house, changed = () => undefined) {
|
|
170
173
|
this.house = house;
|
|
171
174
|
this.changed = changed;
|
|
172
175
|
}
|
|
173
176
|
idle() {
|
|
174
|
-
return this.refreshing === 0;
|
|
177
|
+
return this.refreshing === 0 && this.updating === null;
|
|
175
178
|
}
|
|
176
179
|
read() {
|
|
177
180
|
return this.reading.then(() => undefined, () => undefined);
|
|
@@ -187,7 +190,7 @@ export class Agents {
|
|
|
187
190
|
return this.desired;
|
|
188
191
|
});
|
|
189
192
|
this.reading = reading;
|
|
190
|
-
reading
|
|
193
|
+
this.loaded = reading
|
|
191
194
|
.then(({ agents, transcribe }) => Promise.all([
|
|
192
195
|
holdTranscription(transcribe)
|
|
193
196
|
.then((changed) => (changed ? this.report() : undefined))
|
|
@@ -203,6 +206,10 @@ export class Agents {
|
|
|
203
206
|
});
|
|
204
207
|
return reading.then(() => undefined);
|
|
205
208
|
}
|
|
209
|
+
keepCurrent() {
|
|
210
|
+
this.updateAll(this.loaded);
|
|
211
|
+
setInterval(() => this.updateAll(Promise.resolve()), CLI_UPDATE_MS);
|
|
212
|
+
}
|
|
206
213
|
cli(kind) {
|
|
207
214
|
this.starts += 1;
|
|
208
215
|
return this.queue(kind, async () => {
|
|
@@ -281,9 +288,6 @@ export class Agents {
|
|
|
281
288
|
return signal === undefined ? queued : Promise.race([queued, ended(signal)]);
|
|
282
289
|
}
|
|
283
290
|
async ensure(kind, signal, owner = {}) {
|
|
284
|
-
const failed = await installAdapter(kind, signal);
|
|
285
|
-
if (failed !== null)
|
|
286
|
-
throw new Error(failed);
|
|
287
291
|
const found = await locate(kind, signal);
|
|
288
292
|
if (found !== null)
|
|
289
293
|
return found;
|
|
@@ -302,7 +306,7 @@ export class Agents {
|
|
|
302
306
|
async resolve(kind, signal, owner) {
|
|
303
307
|
let cli;
|
|
304
308
|
try {
|
|
305
|
-
cli = await
|
|
309
|
+
cli = await this.ensure(kind, signal, owner);
|
|
306
310
|
}
|
|
307
311
|
catch (error) {
|
|
308
312
|
const failure = causeOf(error);
|
|
@@ -321,6 +325,35 @@ export class Agents {
|
|
|
321
325
|
if (this.reads.get(kind) === current && !signal?.aborted)
|
|
322
326
|
this.hold(kind, reading);
|
|
323
327
|
}
|
|
328
|
+
updateAll(after) {
|
|
329
|
+
if (this.updating !== null)
|
|
330
|
+
return;
|
|
331
|
+
this.updating = after
|
|
332
|
+
.then(async () => {
|
|
333
|
+
for (const kind of this.desired.agents) {
|
|
334
|
+
if (this.installs.has(kind) || !this.readings.get(kind)?.release)
|
|
335
|
+
continue;
|
|
336
|
+
const failure = await updateCli(kind);
|
|
337
|
+
if (failure !== null)
|
|
338
|
+
process.stderr.write(`kit: ${kind} did not update: ${failure}\n`);
|
|
339
|
+
await this.readVersion(kind);
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
.catch(logged)
|
|
343
|
+
.finally(() => {
|
|
344
|
+
this.updating = null;
|
|
345
|
+
this.changed();
|
|
346
|
+
});
|
|
347
|
+
this.changed();
|
|
348
|
+
}
|
|
349
|
+
async readVersion(kind) {
|
|
350
|
+
const cli = await locate(kind);
|
|
351
|
+
const release = cli === null ? null : await versionOf(cli);
|
|
352
|
+
const current = this.readings.get(kind);
|
|
353
|
+
if (release === null || !current?.release || current.release === release)
|
|
354
|
+
return;
|
|
355
|
+
this.update(kind, { ...current, release });
|
|
356
|
+
}
|
|
324
357
|
async quietOffer(kind, cli, known) {
|
|
325
358
|
for (let seen = -1; seen !== this.starts;) {
|
|
326
359
|
seen = this.starts;
|
|
@@ -347,7 +380,6 @@ export class Agents {
|
|
|
347
380
|
...(form === undefined ? {} : { form }),
|
|
348
381
|
kit_release: KIT_RELEASE,
|
|
349
382
|
wire: KIT_WIRE,
|
|
350
|
-
updates_itself: await updatesItself(),
|
|
351
383
|
agent_base: agentBase(),
|
|
352
384
|
agents,
|
|
353
385
|
transcription: transcriptionReport(),
|
package/dist/building.js
CHANGED
|
@@ -16,10 +16,14 @@ async function served(name, signal) {
|
|
|
16
16
|
await answer.body?.cancel();
|
|
17
17
|
throw new Error(`House answered ${answer.status} for ${name}`);
|
|
18
18
|
}
|
|
19
|
-
export async function
|
|
19
|
+
export async function servedRelease(signal) {
|
|
20
20
|
const manifest = (await (await served(MANIFEST, signal)).json());
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
const installer = manifest.hosts?.linux;
|
|
22
|
+
const sha256 = typeof installer === 'string' ? manifest.files?.[installer]?.sha256 : undefined;
|
|
23
|
+
if (typeof manifest.version !== 'string' || typeof installer !== 'string' || typeof sha256 !== 'string') {
|
|
24
|
+
throw new Error(`House serves no Kit release in ${MANIFEST}`);
|
|
25
|
+
}
|
|
26
|
+
return { version: manifest.version, installer, sha256 };
|
|
23
27
|
}
|
|
24
28
|
export async function downloadServed(name, sha256, destination, signal, most = Infinity) {
|
|
25
29
|
const answer = await served(name, signal);
|
package/dist/clis.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { constants, readFileSync } from 'node:fs';
|
|
4
|
-
import { access, mkdir, readFile, rename,
|
|
4
|
+
import { access, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { homedir, userInfo } from 'node:os';
|
|
6
6
|
import { delimiter, dirname, isAbsolute, join } from 'node:path';
|
|
7
|
-
import {
|
|
8
|
-
import { kitHome } from './home.js';
|
|
7
|
+
import { releaseDirectory } from './placement.js';
|
|
9
8
|
import { childEnvironment } from './relay.js';
|
|
10
9
|
const HOUSE_EXECUTABLES = /^host_executable\(name = "house", paths = (\[.*\])\)$/m;
|
|
11
10
|
function houseRule(houses) {
|
|
@@ -145,25 +144,13 @@ export const CLIS = {
|
|
|
145
144
|
const KIT_PACKAGE = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
146
145
|
export const KIT_RELEASE = KIT_PACKAGE.version;
|
|
147
146
|
const VERSION = /\d+\.\d+\.\d+(?:-[0-9A-Za-z.]+)?/;
|
|
148
|
-
// An install still running after fifteen minutes is ended
|
|
147
|
+
// An install or update still running after fifteen minutes is ended as failed, so a hung download can neither hold its CLI's report nor keep the Kit busy.
|
|
149
148
|
const INSTALL_MS = 15 * 60_000;
|
|
150
149
|
// A login shell or a version read is ended after half a minute, so a profile that waits for input cannot stall the report.
|
|
151
150
|
const READ_MS = 30_000;
|
|
152
151
|
const CAUSE_CHARACTERS = 4000;
|
|
153
|
-
function prefix(kind) {
|
|
154
|
-
return join(kitHome(), 'agents', kind);
|
|
155
|
-
}
|
|
156
152
|
export function adapterCommand(kind) {
|
|
157
|
-
return join(
|
|
158
|
-
}
|
|
159
|
-
async function installedAdapter(kind) {
|
|
160
|
-
try {
|
|
161
|
-
const manifest = join(prefix(kind), 'node_modules', CLIS[kind].adapter.package, 'package.json');
|
|
162
|
-
return JSON.parse(await readFile(manifest, 'utf8')).version;
|
|
163
|
-
}
|
|
164
|
-
catch {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
153
|
+
return join(releaseDirectory(KIT_RELEASE), 'adapters', kind, 'node_modules', '.bin', CLIS[kind].adapter.bin);
|
|
167
154
|
}
|
|
168
155
|
export function run(command, args, timeoutMs, detached = false, signal) {
|
|
169
156
|
return new Promise((resolve) => {
|
|
@@ -207,38 +194,15 @@ export function failureOf(ran, program) {
|
|
|
207
194
|
export function adapterArchive(kind) {
|
|
208
195
|
return `${kind}-${KIT_PACKAGE.peerDependencies[CLIS[kind].adapter.package]}.tar.gz`;
|
|
209
196
|
}
|
|
210
|
-
|
|
211
|
-
const adapter = CLIS[kind].adapter;
|
|
212
|
-
if (adapter === null)
|
|
213
|
-
return null;
|
|
214
|
-
if ((await installedAdapter(kind)) === KIT_PACKAGE.peerDependencies[adapter.package])
|
|
215
|
-
return null;
|
|
216
|
-
const name = adapterArchive(kind);
|
|
217
|
-
const archive = `${prefix(kind)}.tar.gz`;
|
|
197
|
+
async function bounded(program, work, signal) {
|
|
218
198
|
const deadline = AbortSignal.timeout(INSTALL_MS);
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return `House serves no ${name}`;
|
|
224
|
-
await rm(prefix(kind), { recursive: true, force: true });
|
|
225
|
-
await mkdir(prefix(kind), { recursive: true });
|
|
226
|
-
await downloadServed(name, sha256, archive, bounded);
|
|
227
|
-
const ran = await run('tar', ['-xzf', archive, '-C', prefix(kind)], INSTALL_MS, true, signal);
|
|
228
|
-
return ran.status === 0 ? null : failureOf(ran, 'tar');
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
if (deadline.aborted && signal?.aborted !== true)
|
|
232
|
-
return `${name} did not arrive within ${INSTALL_MS / 1000} seconds`;
|
|
233
|
-
return error instanceof Error ? error.message : String(error);
|
|
234
|
-
}
|
|
235
|
-
finally {
|
|
236
|
-
await rm(archive, { force: true });
|
|
237
|
-
}
|
|
199
|
+
const ran = await work(signal === undefined ? deadline : AbortSignal.any([signal, deadline]));
|
|
200
|
+
if (deadline.aborted && signal?.aborted !== true)
|
|
201
|
+
return `${program} did not end within ${INSTALL_MS / 1000} seconds`;
|
|
202
|
+
return ran.status === 0 ? null : failureOf(ran, program);
|
|
238
203
|
}
|
|
239
|
-
export
|
|
240
|
-
|
|
241
|
-
return ran.status === 0 ? null : failureOf(ran, 'the install');
|
|
204
|
+
export function installCli(kind, signal) {
|
|
205
|
+
return bounded('the install', (within) => run('bash', ['-o', 'pipefail', '-c', CLIS[kind].install], INSTALL_MS, true, within), signal);
|
|
242
206
|
}
|
|
243
207
|
function loginShell() {
|
|
244
208
|
return process.env.SHELL || userInfo().shell;
|
|
@@ -246,8 +210,8 @@ function loginShell() {
|
|
|
246
210
|
const FOUND = 'house-kit-cli ';
|
|
247
211
|
const LOOKUP = `found=$(command -v "$1") || exit 0; case $found in /*) ;; *) found=$(pwd -P)/$found ;; esac; printf "${FOUND}%s\\n" "$found"`;
|
|
248
212
|
const USER_BIN = 'case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) PATH="$PATH:$HOME/.local/bin" ;; esac; export PATH';
|
|
249
|
-
function inLoginShell(script, argument = '', signal) {
|
|
250
|
-
return run(loginShell(), ['-l', '-i', '-c', `exec /bin/sh -c '${USER_BIN}; ${script}' sh ${argument}`],
|
|
213
|
+
function inLoginShell(script, argument = '', signal, timeoutMs = READ_MS) {
|
|
214
|
+
return run(loginShell(), ['-l', '-i', '-c', `exec /bin/sh -c '${USER_BIN}; ${script}' sh ${argument}`], timeoutMs, true, signal);
|
|
251
215
|
}
|
|
252
216
|
export async function onLoginPath(program, signal) {
|
|
253
217
|
const ran = await inLoginShell(LOOKUP, program, signal);
|
|
@@ -256,6 +220,9 @@ export async function onLoginPath(program, signal) {
|
|
|
256
220
|
export function locate(kind, signal) {
|
|
257
221
|
return onLoginPath(CLIS[kind].bin, signal);
|
|
258
222
|
}
|
|
223
|
+
export function updateCli(kind) {
|
|
224
|
+
return bounded('the update', (within) => inLoginShell(CLIS[kind].update, '', within, INSTALL_MS));
|
|
225
|
+
}
|
|
259
226
|
export async function loginPath() {
|
|
260
227
|
const marker = 'house-kit-path ';
|
|
261
228
|
const ran = await inLoginShell(`printf "${marker}%s\\n" "$PATH"`);
|
package/dist/connection.js
CHANGED
|
@@ -4,7 +4,7 @@ import { end, REPLACED, signInAgain } from './end.js';
|
|
|
4
4
|
import { readEnrolment } from './home.js';
|
|
5
5
|
import { relayed } from './relay.js';
|
|
6
6
|
import { Silence } from './silence.js';
|
|
7
|
-
import { endUnserved, KIT_WIRE } from './wire.js';
|
|
7
|
+
import { endUnserved, KIT_WIRE, unserved } from './wire.js';
|
|
8
8
|
const PATH = '/kit/stream';
|
|
9
9
|
const SUBPROTOCOL = `house.kit.stream.${KIT_WIRE}`;
|
|
10
10
|
const REFUSAL_HEADER = 'x-house-refusal';
|
|
@@ -32,11 +32,10 @@ subscribe('undici:request:headers', (message) => {
|
|
|
32
32
|
export function holdConnection(handlers) {
|
|
33
33
|
let connection = null;
|
|
34
34
|
let socket = null;
|
|
35
|
-
let paused = false;
|
|
36
35
|
let failures = 0;
|
|
37
36
|
const connect = async () => {
|
|
38
37
|
const { house, credential } = (await readEnrolment());
|
|
39
|
-
if (
|
|
38
|
+
if (socket !== null || unserved())
|
|
40
39
|
return;
|
|
41
40
|
refused = null;
|
|
42
41
|
const opening = new WebSocket(relayed(connectionUrl(house)), {
|
|
@@ -65,8 +64,6 @@ export function holdConnection(handlers) {
|
|
|
65
64
|
endUnserved(refused.text);
|
|
66
65
|
if (!opened && refused !== null)
|
|
67
66
|
process.stderr.write(`kit: ${refused.message}\n`);
|
|
68
|
-
if (paused)
|
|
69
|
-
return;
|
|
70
67
|
// A closed connection reopens in one to three seconds, spread so Kit processes one restart closed return apart; an opening House refused or left unanswered backs off on the jittered curve, retryable or not, since a resident outlives a House deploy.
|
|
71
68
|
setTimeout(() => void connect(), opened ? 1000 + Math.random() * 2000 : retryDelay(failures++));
|
|
72
69
|
};
|
|
@@ -104,13 +101,5 @@ export function holdConnection(handlers) {
|
|
|
104
101
|
connection.send(JSON.stringify(notification));
|
|
105
102
|
return true;
|
|
106
103
|
},
|
|
107
|
-
pause() {
|
|
108
|
-
paused = true;
|
|
109
|
-
socket?.close();
|
|
110
|
-
return () => {
|
|
111
|
-
paused = false;
|
|
112
|
-
void connect();
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
104
|
};
|
|
116
105
|
}
|
package/dist/end.js
CHANGED
|
@@ -14,7 +14,7 @@ export function end(reason) {
|
|
|
14
14
|
export function restart(reason) {
|
|
15
15
|
exit(reason, 1);
|
|
16
16
|
}
|
|
17
|
-
function updateLine(lead) {
|
|
17
|
+
export function updateLine(lead) {
|
|
18
18
|
const command = installCommand();
|
|
19
19
|
return command === null ? `${lead}; install it again.` : `${lead}. Update it: ${command}`;
|
|
20
20
|
}
|
package/dist/events.js
CHANGED
|
@@ -4,8 +4,8 @@ const HIDDEN = { shown: 'hidden' };
|
|
|
4
4
|
const FINISHED = new Set(['completed', 'failed']);
|
|
5
5
|
export const RUNNING_JOB = new Set(['running', 'paused']);
|
|
6
6
|
const MESSAGE_TARGETS = new Map([
|
|
7
|
-
['
|
|
8
|
-
['
|
|
7
|
+
['send_agent_function_request', ['agent_function', 'agent']],
|
|
8
|
+
['cancel_agent_function_request', ['request']],
|
|
9
9
|
['send_mail', ['conversation', 'request']],
|
|
10
10
|
]);
|
|
11
11
|
function words(line) {
|
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/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/resident.js
CHANGED
|
@@ -11,7 +11,7 @@ import { received } from './bridge.js';
|
|
|
11
11
|
import { CONVERSATION_METHODS, Conversations } from './conversations.js';
|
|
12
12
|
import { LocalCopies } from './copies.js';
|
|
13
13
|
import { restart, unknownMethod } from './end.js';
|
|
14
|
-
import { agentBase, enrolled,
|
|
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
17
|
import { removeInputs, Scripts } from './scripts.js';
|
|
@@ -20,6 +20,7 @@ import { SignIns } from './sign-in.js';
|
|
|
20
20
|
import { placeSkillSet } from './skills.js';
|
|
21
21
|
import { holdConnection } from './connection.js';
|
|
22
22
|
import { KitUpdate } from './update.js';
|
|
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,7 +111,6 @@ 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;
|
|
@@ -140,11 +140,8 @@ export async function resident() {
|
|
|
140
140
|
if (!idle()) {
|
|
141
141
|
reported = false;
|
|
142
142
|
}
|
|
143
|
-
else if (
|
|
144
|
-
restart(
|
|
145
|
-
}
|
|
146
|
-
else if (update.held() && conversations.quiet()) {
|
|
147
|
-
update.apply(connection.pause());
|
|
143
|
+
else if (update.placedRelease() !== null && conversations.quiet()) {
|
|
144
|
+
restart(`Kit ${update.placedRelease()} is placed; its supervisor starts it`);
|
|
148
145
|
}
|
|
149
146
|
else if (!reported) {
|
|
150
147
|
reported = true;
|
|
@@ -161,7 +158,9 @@ export async function resident() {
|
|
|
161
158
|
changed,
|
|
162
159
|
});
|
|
163
160
|
const signIns = new SignIns(house, agents, calls);
|
|
164
|
-
const update = new KitUpdate(calls
|
|
161
|
+
const update = new KitUpdate(calls);
|
|
162
|
+
if (process.env.HOUSE_KIT_SANDBOX !== '1')
|
|
163
|
+
takeUnservedRelease((release) => logged(update.takeRefused(release)));
|
|
165
164
|
removeInputs();
|
|
166
165
|
const scripts = new Scripts(calls, (notification) => connection?.send(notification) ?? false);
|
|
167
166
|
for (const signal of ['SIGTERM', 'SIGINT']) {
|
|
@@ -172,8 +171,8 @@ export async function resident() {
|
|
|
172
171
|
});
|
|
173
172
|
}
|
|
174
173
|
await house.deliver('/kit/restarted', {}).catch(report);
|
|
175
|
-
await update.resumed().catch(report);
|
|
176
174
|
logged(agents.refresh());
|
|
175
|
+
agents.keepCurrent();
|
|
177
176
|
connection = holdConnection({
|
|
178
177
|
opened: () => {
|
|
179
178
|
conversations.opened();
|
|
@@ -185,7 +184,7 @@ export async function resident() {
|
|
|
185
184
|
if (call.kind === 'sign_in')
|
|
186
185
|
signIns.start(call);
|
|
187
186
|
else if (call.kind === 'update')
|
|
188
|
-
update.
|
|
187
|
+
update.take(call);
|
|
189
188
|
else if (call.kind === 'run_wizard' || call.kind === 'run_function')
|
|
190
189
|
scripts.start(call);
|
|
191
190
|
else if (CONVERSATION_METHODS.has(call.kind))
|
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;
|
package/dist/update.js
CHANGED
|
@@ -1,181 +1,127 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import {
|
|
3
|
-
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { homedir } from 'node:os';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
5
3
|
import { join } from 'node:path';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
// A
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function unit(callId) {
|
|
26
|
-
return `house-kit-update-${callId}`;
|
|
27
|
-
}
|
|
28
|
-
function manager() {
|
|
29
|
-
return service() === 'user' ? ['--user'] : [];
|
|
30
|
-
}
|
|
31
|
-
function elevated() {
|
|
32
|
-
return throughSudo() ? ['sudo', '-n'] : [];
|
|
33
|
-
}
|
|
34
|
-
function account() {
|
|
35
|
-
return throughSudo() ? [`--uid=${process.getuid()}`, `--gid=${process.getgid()}`] : [];
|
|
36
|
-
}
|
|
37
|
-
function placement() {
|
|
38
|
-
return service() === 'user' ? ['--workspace', workspaceRoot()] : ['--linux'];
|
|
39
|
-
}
|
|
40
|
-
function record() {
|
|
41
|
-
return `outcome=$1; shift; timeout ${RERUN_MS / 1000} bash "$0" "$@"; printf %s "$?" > "$outcome"; ${[...elevated(), 'systemctl', ...manager(), 'start', 'house-kit.service'].join(' ')}`;
|
|
42
|
-
}
|
|
43
|
-
async function pendingCall() {
|
|
44
|
-
try {
|
|
45
|
-
return JSON.parse(await readFile(pending(), 'utf8')).input_id;
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
async function recorded() {
|
|
52
|
-
const status = await readFile(outcome(), 'utf8').catch(() => null);
|
|
53
|
-
if (status === null)
|
|
54
|
-
return null;
|
|
55
|
-
if (status.trim() === TIMED_OUT)
|
|
56
|
-
return { refused: `the Kit bootstrap did not finish within ${RERUN_MS / 1000} seconds` };
|
|
57
|
-
return status.trim() === '0' ? {} : { refused: `the Kit bootstrap exited with ${status.trim()}` };
|
|
58
|
-
}
|
|
59
|
-
async function forget(callId) {
|
|
60
|
-
if ((await pendingCall()) !== callId)
|
|
61
|
-
return;
|
|
62
|
-
await rm(pending(), { force: true });
|
|
63
|
-
await rm(outcome(), { force: true });
|
|
64
|
-
}
|
|
65
|
-
async function finished(callId) {
|
|
66
|
-
for (;;) {
|
|
67
|
-
const result = await recorded();
|
|
68
|
-
if (result !== null)
|
|
69
|
-
return result;
|
|
70
|
-
// systemctl answers at once, so ten seconds only bounds a stuck service manager.
|
|
71
|
-
if ((await run('systemctl', [...manager(), 'is-active', '--quiet', unit(callId)], 10_000)).status !== 0) {
|
|
72
|
-
return (await recorded()) ?? { refused: 'the Kit bootstrap did not finish' };
|
|
73
|
-
}
|
|
74
|
-
// The bootstrap runs for minutes, so checking each second sees its outcome within a second at little cost.
|
|
75
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
async function bounded(body, most) {
|
|
79
|
-
const chunks = [];
|
|
80
|
-
let bytes = 0;
|
|
81
|
-
for await (const chunk of body ?? []) {
|
|
82
|
-
bytes += chunk.length;
|
|
83
|
-
if (bytes > most)
|
|
84
|
-
return null;
|
|
85
|
-
chunks.push(chunk);
|
|
86
|
-
}
|
|
87
|
-
return Buffer.concat(chunks, bytes);
|
|
88
|
-
}
|
|
89
|
-
function rerun(script, callId, flags) {
|
|
4
|
+
import { downloadServed, servedRelease } from './building.js';
|
|
5
|
+
import { KIT_RELEASE } from './clis.js';
|
|
6
|
+
import { restart, updateLine } from './end.js';
|
|
7
|
+
import { kitHome, readEnrolment } from './home.js';
|
|
8
|
+
import { placedPrefix } from './placement.js';
|
|
9
|
+
import { childEnvironment, relayOrigin } from './relay.js';
|
|
10
|
+
const INSTALLER_BYTES = 1_000_000;
|
|
11
|
+
// A release's Node.js, Kit package and adapters are about a hundred megabytes, so an hour carries them at thirty kilobytes a second, and a placement still running then has hung.
|
|
12
|
+
const PLACEMENT_MS = 60 * 60_000;
|
|
13
|
+
// A refused Kit is offline until its placement succeeds, so it tries again within five minutes of its Building or this computer recovering, while a placement that fails only after its hundred-megabyte download runs at most twelve times an hour.
|
|
14
|
+
export const PLACEMENT_RETRY_MS = 5 * 60_000;
|
|
15
|
+
const REFUSED = 'kit_bootstrap_refused: ';
|
|
16
|
+
const OUTPUT_CHARACTERS = 4000;
|
|
17
|
+
function causeOf(error) {
|
|
18
|
+
if (error instanceof TypeError && error.cause instanceof Error)
|
|
19
|
+
return `the Building did not answer: ${error.cause.message}`;
|
|
20
|
+
return error instanceof Error ? error.message : String(error);
|
|
21
|
+
}
|
|
22
|
+
function placing(installer, house, prefix, signal) {
|
|
90
23
|
return new Promise((resolve) => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
'
|
|
94
|
-
...
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
24
|
+
const relay = relayOrigin();
|
|
25
|
+
const child = spawn('bash', [installer, '--house', house, '--place', prefix], {
|
|
26
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
27
|
+
env: { ...childEnvironment(), ...(relay === undefined ? {} : { HOUSE_KIT_RELAY: relay }) },
|
|
28
|
+
detached: true,
|
|
29
|
+
});
|
|
30
|
+
const stop = () => {
|
|
31
|
+
try {
|
|
32
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
};
|
|
36
|
+
signal.addEventListener('abort', stop, { once: true });
|
|
37
|
+
if (signal.aborted)
|
|
38
|
+
stop();
|
|
39
|
+
let output = '';
|
|
40
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
41
|
+
stream.setEncoding('utf8').on('data', (chunk) => {
|
|
42
|
+
output = (output + chunk).slice(-OUTPUT_CHARACTERS);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const ended = (failure) => {
|
|
46
|
+
signal.removeEventListener('abort', stop);
|
|
47
|
+
resolve(failure);
|
|
48
|
+
};
|
|
49
|
+
child.on('error', (error) => ended(error.message));
|
|
50
|
+
child.on('close', (status) => {
|
|
51
|
+
if (status === 0)
|
|
52
|
+
return ended(null);
|
|
53
|
+
if (signal.aborted)
|
|
54
|
+
return ended(`the placement did not end within ${PLACEMENT_MS / 1000} seconds`);
|
|
55
|
+
const lines = output.split('\n').filter((line) => line.trim() !== '');
|
|
56
|
+
const refused = lines.findLast((line) => line.startsWith(REFUSED));
|
|
57
|
+
ended(refused?.slice(REFUSED.length) ?? lines.at(-1) ?? `the placement ended with status ${status ?? 'a signal'}`);
|
|
58
|
+
});
|
|
113
59
|
});
|
|
114
60
|
}
|
|
115
61
|
export class KitUpdate {
|
|
116
62
|
calls;
|
|
117
|
-
changed;
|
|
118
|
-
waiting = new Map();
|
|
119
63
|
queue = Promise.resolve();
|
|
120
|
-
|
|
64
|
+
placed = null;
|
|
65
|
+
constructor(calls) {
|
|
121
66
|
this.calls = calls;
|
|
122
|
-
this.changed = changed;
|
|
123
67
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (callId === null)
|
|
127
|
-
return;
|
|
128
|
-
await this.enqueue(callId, () => finished(callId));
|
|
68
|
+
take(call) {
|
|
69
|
+
void this.calls.carry(call.input_id, async () => ({ result: await this.queued(() => this.update(call.release)) }));
|
|
129
70
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
71
|
+
async takeRefused(release) {
|
|
72
|
+
for (;;) {
|
|
73
|
+
const failure = await this.queued(async () => (this.placed === null ? this.place(release, 'served') : null));
|
|
74
|
+
if (failure === null)
|
|
75
|
+
restart(`Kit ${this.placed} is placed; its supervisor starts it`);
|
|
76
|
+
process.stderr.write(`kit: ${updateLine(`Kit ${KIT_RELEASE} stays offline and tries again in ${PLACEMENT_RETRY_MS / 60_000} minutes`)}\n`);
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, PLACEMENT_RETRY_MS));
|
|
78
|
+
}
|
|
133
79
|
}
|
|
134
|
-
|
|
135
|
-
return this.
|
|
80
|
+
placedRelease() {
|
|
81
|
+
return this.placed;
|
|
136
82
|
}
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
this.
|
|
140
|
-
|
|
83
|
+
queued(work) {
|
|
84
|
+
const taken = this.queue.then(work);
|
|
85
|
+
this.queue = taken.catch(() => undefined);
|
|
86
|
+
return taken;
|
|
141
87
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
88
|
+
async update(release) {
|
|
89
|
+
if (release === (this.placed ?? KIT_RELEASE))
|
|
90
|
+
return {};
|
|
91
|
+
if (this.placed !== null)
|
|
92
|
+
return { refused: `Kit ${this.placed} is placed and starts first, once no turn is open` };
|
|
93
|
+
const failure = await this.place(release, 'named');
|
|
94
|
+
if (failure !== null)
|
|
95
|
+
return { refused: failure };
|
|
96
|
+
process.stderr.write(`kit: Kit ${release} is placed; it starts once no turn is open\n`);
|
|
97
|
+
return {};
|
|
150
98
|
}
|
|
151
|
-
async
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
let script;
|
|
99
|
+
async place(release, which) {
|
|
100
|
+
const signal = AbortSignal.timeout(PLACEMENT_MS);
|
|
101
|
+
let version = release;
|
|
102
|
+
let failure;
|
|
156
103
|
try {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
104
|
+
const prefix = placedPrefix();
|
|
105
|
+
const served = await servedRelease(signal);
|
|
106
|
+
if (which === 'named' && served.version !== release)
|
|
107
|
+
throw new Error(`the Building serves Kit ${served.version}, not Kit ${release}`);
|
|
108
|
+
version = served.version;
|
|
109
|
+
if (version === KIT_RELEASE)
|
|
110
|
+
throw new Error(`the Building serves Kit ${KIT_RELEASE}, which this Kit runs`);
|
|
111
|
+
await mkdir(join(kitHome(), 'update'), { recursive: true, mode: 0o700 });
|
|
112
|
+
const installer = join(kitHome(), 'update', served.installer);
|
|
113
|
+
await downloadServed(served.installer, served.sha256, installer, signal, INSTALLER_BYTES);
|
|
114
|
+
failure = await placing(installer, (await readEnrolment()).house, prefix, signal);
|
|
161
115
|
}
|
|
162
116
|
catch (error) {
|
|
163
|
-
|
|
164
|
-
throw error;
|
|
165
|
-
return { refused: `the Kit bootstrap did not arrive within ${BOOTSTRAP_MS / 1000} seconds` };
|
|
117
|
+
failure = signal.aborted ? `the placement did not end within ${PLACEMENT_MS / 1000} seconds` : causeOf(error);
|
|
166
118
|
}
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return
|
|
119
|
+
if (failure !== null) {
|
|
120
|
+
const refused = `Kit ${version} could not be placed: ${failure}`;
|
|
121
|
+
process.stderr.write(`kit: ${refused}\n`);
|
|
122
|
+
return refused;
|
|
171
123
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
await writeFile(path, script, { mode: 0o700 });
|
|
175
|
-
await rm(outcome(), { force: true });
|
|
176
|
-
await writeFile(pending(), `${JSON.stringify({ input_id: call.input_id })}\n`, { mode: 0o600 });
|
|
177
|
-
return (await rerun(path, call.input_id, call.update_clis ? ['--update-clis'] : [])) ?? (await recorded()) ?? {
|
|
178
|
-
refused: 'the Kit bootstrap did not finish',
|
|
179
|
-
};
|
|
124
|
+
this.placed = version;
|
|
125
|
+
return null;
|
|
180
126
|
}
|
|
181
127
|
}
|
package/dist/wire.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { newerThanHouse, updateRequired } from './end.js';
|
|
2
2
|
import { machine } from './placement.js';
|
|
3
3
|
import { refusalOf } from './refusals.js';
|
|
4
|
-
export const KIT_WIRE =
|
|
4
|
+
export const KIT_WIRE = 4;
|
|
5
5
|
export const KIT_WIRE_HEADER = 'x-house-kit-wire';
|
|
6
|
-
|
|
6
|
+
let take = null;
|
|
7
|
+
let taken = false;
|
|
8
|
+
function unservedBy(text) {
|
|
7
9
|
let body;
|
|
8
10
|
try {
|
|
9
11
|
body = JSON.parse(text);
|
|
@@ -11,12 +13,28 @@ function newer(text) {
|
|
|
11
13
|
catch {
|
|
12
14
|
return null;
|
|
13
15
|
}
|
|
14
|
-
|
|
16
|
+
const error = body?.error;
|
|
17
|
+
if (error?.code !== 'kit_update_required' && error?.code !== 'kit_newer_than_house')
|
|
18
|
+
return null;
|
|
19
|
+
return typeof error.release === 'string' ? error : null;
|
|
20
|
+
}
|
|
21
|
+
export function takeUnservedRelease(taking) {
|
|
22
|
+
take = taking;
|
|
23
|
+
}
|
|
24
|
+
export function unserved() {
|
|
25
|
+
return taken;
|
|
15
26
|
}
|
|
16
27
|
export function endUnserved(text) {
|
|
28
|
+
const refused = unservedBy(text);
|
|
29
|
+
if (refused !== null && take !== null) {
|
|
30
|
+
if (!taken) {
|
|
31
|
+
taken = true;
|
|
32
|
+
take(refused.release);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
17
36
|
if (refusalOf(text)?.code === 'kit_update_required')
|
|
18
37
|
updateRequired();
|
|
19
|
-
|
|
20
|
-
if (refused !== null)
|
|
38
|
+
if (refused?.code === 'kit_newer_than_house')
|
|
21
39
|
newerThanHouse(refused.release, refused.commands[machine()]);
|
|
22
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentshouse/kit",
|
|
3
|
-
"version": "0.2.1-
|
|
3
|
+
"version": "0.2.1-cab5821cf0a77",
|
|
4
4
|
"description": "Kit connects an Environment to agents.house.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@agentclientprotocol/sdk": "2.0.1",
|
|
28
28
|
"@agentshouse/mdmodel": "0.1.0-c4f4d37bc9d6a",
|
|
29
|
-
"@agentshouse/skills": "0.1.0-
|
|
29
|
+
"@agentshouse/skills": "0.1.0-c4476617b690f",
|
|
30
30
|
"@lydell/node-pty": "1.2.0-beta.15",
|
|
31
31
|
"yaml": "2.9.1"
|
|
32
32
|
},
|
package/dist/clis-main.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { post } from './api.js';
|
|
2
|
-
import { CLIS, below, locate, versionOf } from './clis.js';
|
|
3
|
-
import { readEnrolment } from './home.js';
|
|
4
|
-
import { proxyUnlessRelayed } from './relay.js';
|
|
5
|
-
proxyUnlessRelayed();
|
|
6
|
-
try {
|
|
7
|
-
const { house, credential } = (await readEnrolment());
|
|
8
|
-
const { agents } = await post(house, '/kit/agents/desired', {}, credential);
|
|
9
|
-
for (const kind of agents) {
|
|
10
|
-
const cli = await locate(kind);
|
|
11
|
-
if (cli === null)
|
|
12
|
-
continue;
|
|
13
|
-
const release = await versionOf(cli);
|
|
14
|
-
if (release === null)
|
|
15
|
-
continue;
|
|
16
|
-
const { bin, minimum } = CLIS[kind];
|
|
17
|
-
process.stdout.write(`${[bin, cli, release, minimum, below(release, minimum) ? 'old' : 'current'].join('\t')}\n`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
catch (error) {
|
|
21
|
-
process.stderr.write(`kit: ${error.message}\n`);
|
|
22
|
-
process.exitCode = 1;
|
|
23
|
-
}
|