@agentshouse/kit 0.2.1-c4beedfd8b549 → 0.2.1-c4d151f197958
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 +7 -6
- package/dist/api.js +3 -3
- package/dist/bootstrap.json +4 -4
- package/dist/clis.js +1 -1
- package/dist/login.js +22 -101
- package/dist/logout.js +20 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,15 +33,16 @@ them out; running it again without the flag brings them back.
|
|
|
33
33
|
|
|
34
34
|
## Commands
|
|
35
35
|
|
|
36
|
-
`kit login` opens a link you confirm in
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
`kit login` opens or prints a link you confirm in any browser, on this
|
|
37
|
+
computer or another, continues on its own and keeps the Environment's
|
|
38
|
+
credential in `~/.house-kit`, or in `$HOUSE_KIT_HOME` when it is set. Pass
|
|
39
|
+
`--environment <id>` to reconnect an Environment you already have. It also
|
|
40
40
|
connects your own agent: outside an Agent conversation, `house <tool>` calls
|
|
41
41
|
House as you and `git` clones and pushes your Apps at `<house>/app/` as you,
|
|
42
42
|
until you revoke that connection on Connections. `house help` lists the Tools.
|
|
43
|
-
`kit logout` disconnects this computer, stops House Kit and ends
|
|
44
|
-
|
|
43
|
+
`kit logout` disconnects this computer, stops House Kit and ends your own
|
|
44
|
+
agent's connection, so `house` and `git` no longer act as you here; a later
|
|
45
|
+
`kit login` reconnects the same Environment.
|
|
45
46
|
|
|
46
47
|
`kit resident` keeps the Environment connected, and keeps the Rooms you chose
|
|
47
48
|
for this Environment as Git repositories under `local-copies` in the
|
package/dist/api.js
CHANGED
|
@@ -19,7 +19,7 @@ export function retryDelay(attempt) {
|
|
|
19
19
|
// Full jitter, doubling from one second to a 30-second cap, spreads the Kits one outage dropped and retries within half a minute of House's return.
|
|
20
20
|
return Math.random() * Math.min(30_000, 1000 * 2 ** attempt);
|
|
21
21
|
}
|
|
22
|
-
export async function retrying(sent, signal) {
|
|
22
|
+
export async function retrying(sent, signal, pause = retryDelay) {
|
|
23
23
|
for (let attempt = 0;; attempt++) {
|
|
24
24
|
try {
|
|
25
25
|
return await sent();
|
|
@@ -28,8 +28,8 @@ export async function retrying(sent, signal) {
|
|
|
28
28
|
if (!(error instanceof HouseRefusal) || retryable(error.text) !== true)
|
|
29
29
|
throw error;
|
|
30
30
|
}
|
|
31
|
-
// A request House refused as retryable is sent again
|
|
32
|
-
await delay(
|
|
31
|
+
// A request House refused as retryable is sent again after the caller's pause, the jittered curve unless it names its own.
|
|
32
|
+
await delay(pause(attempt), undefined, { signal });
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
export async function post(house, path, body, credential, signal) {
|
package/dist/bootstrap.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
|
-
"version": "0.2.1-
|
|
3
|
+
"version": "0.2.1-c4d151f197958",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"macos": {
|
|
6
6
|
"asset": {
|
|
7
7
|
"name": "connect-linux.sh",
|
|
8
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
8
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c4d151f197958/connect-linux.sh"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"linux": {
|
|
12
12
|
"asset": {
|
|
13
13
|
"name": "connect-linux.sh",
|
|
14
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
14
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c4d151f197958/connect-linux.sh"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"windows": {
|
|
18
18
|
"asset": {
|
|
19
19
|
"name": "connect-windows.ps1",
|
|
20
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-
|
|
20
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c4d151f197958/connect-windows.ps1"
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}
|
package/dist/clis.js
CHANGED
|
@@ -90,7 +90,7 @@ export const CLIS = {
|
|
|
90
90
|
minimum: '1.0.46',
|
|
91
91
|
install: 'curl -fsSL https://x.ai/cli/install.sh | bash',
|
|
92
92
|
adapter: null,
|
|
93
|
-
args: ['agent', '--always-approve', '--no-leader', 'stdio'],
|
|
93
|
+
args: ['--sandbox', 'off', 'agent', '--always-approve', '--no-leader', 'stdio'],
|
|
94
94
|
fullAccess: null,
|
|
95
95
|
sessionMeta: () => ({}),
|
|
96
96
|
renames: false,
|
package/dist/login.js
CHANGED
|
@@ -1,54 +1,19 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
1
2
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
-
import { createServer } from 'node:http';
|
|
3
3
|
import { hostname } from 'node:os';
|
|
4
|
-
import { createInterface } from 'node:readline/promises';
|
|
5
4
|
import { parseArgs } from 'node:util';
|
|
6
|
-
import { post } from './api.js';
|
|
5
|
+
import { post, retrying } from './api.js';
|
|
7
6
|
import { placeAppGit } from './app-git.js';
|
|
8
7
|
import { forgetOwnAgent, readDisconnected, readEnrolment, readOwnAgent, writeEnrolment, writeOwnAgent } from './home.js';
|
|
9
8
|
import { callHouse, rpc } from './mcp.js';
|
|
10
9
|
const DEFAULT_HOUSE = 'https://agents.house';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const server = createServer((request, response) => {
|
|
19
|
-
const received = new URL(request.url ?? '/', 'http://127.0.0.1').searchParams.get('code');
|
|
20
|
-
if (received === null || held !== null) {
|
|
21
|
-
response.writeHead(404).end();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
held = response;
|
|
25
|
-
deliver(received);
|
|
26
|
-
});
|
|
27
|
-
const close = () => {
|
|
28
|
-
server.close();
|
|
29
|
-
server.closeAllConnections();
|
|
30
|
-
};
|
|
31
|
-
const port = Number(process.env.HOUSE_KIT_LOGIN_PORT ?? 0);
|
|
32
|
-
server.once('error', reject);
|
|
33
|
-
server.listen(port, port === 0 ? '127.0.0.1' : '0.0.0.0', () => {
|
|
34
|
-
const address = server.address();
|
|
35
|
-
if (address === null || typeof address === 'string') {
|
|
36
|
-
reject(new Error('the login callback has no port'));
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
resolve({
|
|
40
|
-
uri: `http://127.0.0.1:${address.port}/callback`,
|
|
41
|
-
code,
|
|
42
|
-
answer: (text) => new Promise((written) => {
|
|
43
|
-
if (held === null || held.destroyed || !held.socket || held.socket.destroyed)
|
|
44
|
-
return written();
|
|
45
|
-
held.once('close', () => written());
|
|
46
|
-
held.writeHead(200, { 'content-type': 'text/plain; charset=utf-8', connection: 'close' }).end(`${text}\n`);
|
|
47
|
-
}),
|
|
48
|
-
close,
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
});
|
|
10
|
+
const OPENER = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
11
|
+
// Polling every two seconds brings the User's confirmation to the waiting Kit moments after they give it.
|
|
12
|
+
const POLL_INTERVAL_MS = 2000;
|
|
13
|
+
function open(link) {
|
|
14
|
+
spawn(OPENER, [link], { detached: true, stdio: 'ignore' })
|
|
15
|
+
.on('error', () => undefined)
|
|
16
|
+
.unref();
|
|
52
17
|
}
|
|
53
18
|
async function accepted(house, credential) {
|
|
54
19
|
const checked = await callHouse(house, credential, rpc('tools/list', {}));
|
|
@@ -58,25 +23,12 @@ async function accepted(house, credential) {
|
|
|
58
23
|
return false;
|
|
59
24
|
throw new Error(`House answered ${checked.status} when Kit checked this computer's own agent's connection: ${checked.text}`);
|
|
60
25
|
}
|
|
61
|
-
async function typed(question) {
|
|
62
|
-
const reader = createInterface({ input: process.stdin, output: process.stdout });
|
|
63
|
-
const closed = new Promise((_, reject) => {
|
|
64
|
-
reader.once('close', () => reject(new Error('no code was typed')));
|
|
65
|
-
});
|
|
66
|
-
try {
|
|
67
|
-
return (await Promise.race([reader.question(question), closed])).trim();
|
|
68
|
-
}
|
|
69
|
-
finally {
|
|
70
|
-
reader.close();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
26
|
export async function login(argv) {
|
|
74
27
|
const { values } = parseArgs({
|
|
75
28
|
args: argv,
|
|
76
29
|
options: {
|
|
77
30
|
house: { type: 'string' },
|
|
78
31
|
environment: { type: 'string' },
|
|
79
|
-
manual: { type: 'boolean', default: false },
|
|
80
32
|
},
|
|
81
33
|
});
|
|
82
34
|
const enrolled = (await readEnrolment()) ?? (await readDisconnected());
|
|
@@ -86,55 +38,24 @@ export async function login(argv) {
|
|
|
86
38
|
const ownAgent = await readOwnAgent();
|
|
87
39
|
const held = ownAgent !== null && ownAgent.house === house && (await accepted(house, ownAgent.credential));
|
|
88
40
|
const verifier = randomBytes(32).toString('base64url');
|
|
89
|
-
const started = {
|
|
41
|
+
const started = await retrying(() => post(house, '/kit', {
|
|
90
42
|
...(environment === undefined
|
|
91
43
|
? { act: 'new_environment', label: hostname(), ...(replaces === undefined ? {} : { replaces }) }
|
|
92
44
|
: { act: 'existing_environment', environment_id: environment }),
|
|
93
45
|
code_challenge: createHash('sha256').update(verifier).digest('base64url'),
|
|
94
46
|
...(held ? {} : { headless: true }),
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
else if (held && ownAgent.user !== exchanged.user) {
|
|
104
|
-
await forgetOwnAgent();
|
|
105
|
-
process.stdout.write("This computer's own agent was another User's; run kit login again to connect it to you.\n");
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
const placeGit = async () => placeAppGit(ownAgent, await readOwnAgent());
|
|
109
|
-
if (values.manual) {
|
|
110
|
-
const { start_url } = await post(house, '/kit', {
|
|
111
|
-
...started,
|
|
112
|
-
presentation: 'manual',
|
|
113
|
-
});
|
|
114
|
-
process.stdout.write(`Open this link and confirm: ${start_url}\n`);
|
|
115
|
-
await exchange(await typed('Code: '));
|
|
116
|
-
await placeGit();
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
const callback = await listen();
|
|
120
|
-
try {
|
|
121
|
-
const { start_url } = await post(house, '/kit', {
|
|
122
|
-
...started,
|
|
123
|
-
presentation: 'loopback',
|
|
124
|
-
loopback_uri: callback.uri,
|
|
125
|
-
});
|
|
126
|
-
process.stdout.write(`Open this link and confirm: ${start_url}\n`);
|
|
127
|
-
try {
|
|
128
|
-
await exchange(await callback.code);
|
|
129
|
-
}
|
|
130
|
-
catch (error) {
|
|
131
|
-
await callback.answer(`This Environment was not connected: ${error.message}`);
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
await callback.answer('This Environment is connected. You can close this tab.');
|
|
47
|
+
}));
|
|
48
|
+
process.stdout.write(`Open this link and confirm: ${started.start_url}\n`);
|
|
49
|
+
open(started.start_url);
|
|
50
|
+
const exchanged = await retrying(() => post(house, '/kit/token', { login: started.login, code_verifier: verifier }), undefined, () => POLL_INTERVAL_MS);
|
|
51
|
+
await writeEnrolment({ house, environment: exchanged.environment, credential: exchanged.credential });
|
|
52
|
+
process.stdout.write(`Environment ${exchanged.environment} is connected to ${house}.\n`);
|
|
53
|
+
if (exchanged.headless_credential !== undefined) {
|
|
54
|
+
await writeOwnAgent({ house, user: exchanged.user, credential: exchanged.headless_credential });
|
|
135
55
|
}
|
|
136
|
-
|
|
137
|
-
|
|
56
|
+
else if (held && ownAgent.user !== exchanged.user) {
|
|
57
|
+
await forgetOwnAgent();
|
|
58
|
+
process.stdout.write("This computer's own agent was another User's; run kit login again to connect it to you.\n");
|
|
138
59
|
}
|
|
139
|
-
await
|
|
60
|
+
await placeAppGit(ownAgent, await readOwnAgent());
|
|
140
61
|
}
|
package/dist/logout.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import { HouseRefusal, post } from './api.js';
|
|
2
2
|
import { forgetAppGit } from './app-git.js';
|
|
3
|
-
import { forgetEnrolment, readEnrolment } from './home.js';
|
|
3
|
+
import { forgetEnrolment, forgetOwnAgent, readEnrolment, readOwnAgent } from './home.js';
|
|
4
|
+
const KEPT_CONNECTION = "House had already disconnected this computer, so its own agent's connection stays until you revoke it on Connections.\n";
|
|
4
5
|
export async function logout() {
|
|
5
6
|
const enrolled = await readEnrolment();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const ownAgent = await readOwnAgent();
|
|
8
|
+
let refused = false;
|
|
9
|
+
if (enrolled !== null) {
|
|
10
|
+
try {
|
|
11
|
+
await post(enrolled.house, '/kit/logout', {}, enrolled.credential);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (!(error instanceof HouseRefusal && error.status === 401))
|
|
15
|
+
throw error;
|
|
16
|
+
refused = true;
|
|
17
|
+
}
|
|
18
|
+
await forgetEnrolment(enrolled);
|
|
10
19
|
}
|
|
11
|
-
try {
|
|
12
|
-
await post(enrolled.house, '/kit/logout', {}, enrolled.credential);
|
|
13
|
-
}
|
|
14
|
-
catch (error) {
|
|
15
|
-
if (!(error instanceof HouseRefusal && error.status === 401))
|
|
16
|
-
throw error;
|
|
17
|
-
}
|
|
18
|
-
await forgetEnrolment(enrolled);
|
|
19
20
|
await forgetAppGit();
|
|
20
|
-
|
|
21
|
+
await forgetOwnAgent();
|
|
22
|
+
process.stdout.write(enrolled === null
|
|
23
|
+
? 'This computer is not connected to House.\n'
|
|
24
|
+
: `Environment ${enrolled.environment} is disconnected; kit login reconnects it.\n`);
|
|
25
|
+
if (ownAgent !== null && (enrolled === null || refused))
|
|
26
|
+
process.stdout.write(KEPT_CONNECTION);
|
|
21
27
|
}
|