@agentshouse/kit 0.2.1-alpha.16 → 0.2.1-alpha.18
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 +3 -1
- package/dist/acp.js +1 -1
- package/dist/bootstrap.json +5 -5
- package/dist/bridge.js +10 -38
- package/dist/clis.js +12 -2
- package/dist/home.js +11 -1
- package/dist/house-main.js +27 -17
- package/dist/login.js +20 -1
- package/dist/mcp.js +38 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,7 +38,9 @@ flag brings them back.
|
|
|
38
38
|
`kit login` opens a link you confirm in your browser and keeps the
|
|
39
39
|
Environment's credential in `~/.house-kit`, or in `$HOUSE_KIT_HOME` when it is
|
|
40
40
|
set. Pass `--manual` to paste the code by hand on a machine without a browser,
|
|
41
|
-
and `--environment <id>` to reconnect an Environment you already have.
|
|
41
|
+
and `--environment <id>` to reconnect an Environment you already have. It also
|
|
42
|
+
connects your own agent: `house <tool>` run outside an Agent conversation calls
|
|
43
|
+
House as you, until you revoke that connection on Connections.
|
|
42
44
|
|
|
43
45
|
`kit resident` keeps the Environment connected, and keeps the Rooms you chose
|
|
44
46
|
for this Environment as Git repositories under `working-copies` in the
|
package/dist/acp.js
CHANGED
|
@@ -9,7 +9,7 @@ export async function startAdapter(kind, cli, cwd, app, jobs = () => undefined,
|
|
|
9
9
|
const adapter = CLIS[kind].adapter;
|
|
10
10
|
const child = spawn(adapter === null ? cli : adapterCommand(kind), CLIS[kind].args, {
|
|
11
11
|
cwd,
|
|
12
|
-
env: { ...withoutProxy(), ...(adapter === null ? {} : { [adapter.executable]: cli }), ...added },
|
|
12
|
+
env: { ...withoutProxy(), ...(adapter === null ? {} : { ...adapter.env, [adapter.executable]: cli }), ...added },
|
|
13
13
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
14
|
detached: true,
|
|
15
15
|
});
|
package/dist/bootstrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.2.1-alpha.
|
|
3
|
+
"version": "0.2.1-alpha.18",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"linux": {
|
|
6
|
-
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.
|
|
6
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.18/connect-linux.sh)",
|
|
7
7
|
"asset": {
|
|
8
8
|
"name": "connect-linux.sh",
|
|
9
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.
|
|
9
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.18/connect-linux.sh"
|
|
10
10
|
},
|
|
11
11
|
"workspace": {
|
|
12
12
|
"argument": "--workspace",
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"windows": {
|
|
20
|
-
"command": "& ([scriptblock]::Create((irm https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.
|
|
20
|
+
"command": "& ([scriptblock]::Create((irm https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.18/connect-windows.ps1)))",
|
|
21
21
|
"asset": {
|
|
22
22
|
"name": "connect-windows.ps1",
|
|
23
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.
|
|
23
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-alpha.18/connect-windows.ps1"
|
|
24
24
|
},
|
|
25
25
|
"workspace": {
|
|
26
26
|
"argument": "--workspace",
|
package/dist/bridge.js
CHANGED
|
@@ -4,15 +4,15 @@ import { setTimeout as delay } from 'node:timers/promises';
|
|
|
4
4
|
import { mkdir } from 'node:fs/promises';
|
|
5
5
|
import { createServer, request as httpRequest } from 'node:http';
|
|
6
6
|
import { request as httpsRequest } from 'node:https';
|
|
7
|
-
import { delimiter, join } from 'node:path';
|
|
7
|
+
import { delimiter, dirname, join } from 'node:path';
|
|
8
8
|
import { parse } from 'yaml';
|
|
9
9
|
import { HouseRefusal, retryDelay } from './api.js';
|
|
10
|
-
import {
|
|
10
|
+
import { loginPath } from './clis.js';
|
|
11
11
|
import { changedPaths, } from './copies.js';
|
|
12
12
|
import { kitHome, readEnrolment } from './home.js';
|
|
13
|
+
import { callHouse, mcpBody, rpc, UNREACHABLE } from './mcp.js';
|
|
13
14
|
import { operationId, uuidOf } from './operation.js';
|
|
14
15
|
import { refusalOf } from './refusals.js';
|
|
15
|
-
const MCP_PROTOCOL_VERSION = '2026-07-28';
|
|
16
16
|
const MCP_REQUEST_BYTES = 4 * 1024 * 1024;
|
|
17
17
|
const ATTACHMENTS = '/kit/attachments/upload';
|
|
18
18
|
export async function received(request) {
|
|
@@ -34,7 +34,6 @@ function relay(request, response, target, authorization) {
|
|
|
34
34
|
forwarded.on('error', () => response.destroy());
|
|
35
35
|
request.pipe(forwarded);
|
|
36
36
|
}
|
|
37
|
-
const UNREACHABLE = 'House is unreachable; call again in a minute';
|
|
38
37
|
function refusalIn(text, status) {
|
|
39
38
|
return refusalOf(text) ?? { code: '', text: text === '' ? `House refused it with ${status}` : text, conflicts: [] };
|
|
40
39
|
}
|
|
@@ -87,38 +86,11 @@ function editAnswer(forwarded) {
|
|
|
87
86
|
};
|
|
88
87
|
}
|
|
89
88
|
export async function openBridge(house, copies, conversationId, signal) {
|
|
89
|
+
const path = [join(kitHome(), 'shim'), dirname(process.execPath), await loginPath()].join(delimiter);
|
|
90
90
|
const origin = (await readEnrolment()).house;
|
|
91
91
|
const { credential } = await house.post(`/kit/conversations/${conversationId}/credential`, {}, signal);
|
|
92
92
|
const closed = new AbortController();
|
|
93
|
-
const
|
|
94
|
-
...message,
|
|
95
|
-
params: {
|
|
96
|
-
...message.params,
|
|
97
|
-
_meta: {
|
|
98
|
-
'io.modelcontextprotocol/protocolVersion': MCP_PROTOCOL_VERSION,
|
|
99
|
-
'io.modelcontextprotocol/clientInfo': { name: '@agentshouse/kit', version: KIT_VERSION },
|
|
100
|
-
'io.modelcontextprotocol/clientCapabilities': {},
|
|
101
|
-
...(operation === null ? {} : { 'agents.house/agent-operation': operation }),
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
const sent = async (message, operation) => {
|
|
106
|
-
const tool = message.method === 'tools/call' ? String(message.params?.name) : null;
|
|
107
|
-
const answer = await fetch(new URL('/', origin), {
|
|
108
|
-
method: 'POST',
|
|
109
|
-
headers: {
|
|
110
|
-
'content-type': 'application/json',
|
|
111
|
-
authorization: `Bearer ${credential}`,
|
|
112
|
-
accept: 'application/json, text/event-stream',
|
|
113
|
-
'mcp-protocol-version': MCP_PROTOCOL_VERSION,
|
|
114
|
-
'mcp-method': message.method,
|
|
115
|
-
...(tool === null ? {} : { 'mcp-name': tool }),
|
|
116
|
-
},
|
|
117
|
-
body: body(message, operation),
|
|
118
|
-
signal: closed.signal,
|
|
119
|
-
});
|
|
120
|
-
return { status: answer.status, text: await answer.text() };
|
|
121
|
-
};
|
|
93
|
+
const sent = (message, operation) => callHouse(origin, credential, message, operation, closed.signal);
|
|
122
94
|
const send = async (message, operation) => {
|
|
123
95
|
if (operation === null)
|
|
124
96
|
return sent(message, null);
|
|
@@ -134,7 +106,7 @@ export async function openBridge(house, copies, conversationId, signal) {
|
|
|
134
106
|
await delay(after, undefined, { signal: closed.signal });
|
|
135
107
|
}
|
|
136
108
|
};
|
|
137
|
-
const call = (name, args) => (
|
|
109
|
+
const call = (name, args) => rpc('tools/call', { name, arguments: args });
|
|
138
110
|
const staged = async (operation, changes) => {
|
|
139
111
|
const bytes = Buffer.from(JSON.stringify({ changes }));
|
|
140
112
|
const sha256 = createHash('sha256').update(bytes).digest('hex');
|
|
@@ -175,7 +147,7 @@ export async function openBridge(house, copies, conversationId, signal) {
|
|
|
175
147
|
const forward = async (path, text) => {
|
|
176
148
|
if (path === '/') {
|
|
177
149
|
const message = JSON.parse(text);
|
|
178
|
-
const tool = message.method === 'tools/call' ? String(message.params
|
|
150
|
+
const tool = message.method === 'tools/call' ? String(message.params.name) : null;
|
|
179
151
|
return send(message, tool !== null && (await writes(tool)) ? operationId() : null);
|
|
180
152
|
}
|
|
181
153
|
const answer = await fetch(new URL(path, origin), {
|
|
@@ -191,7 +163,7 @@ export async function openBridge(house, copies, conversationId, signal) {
|
|
|
191
163
|
submit: async (operation, changes, held, retain) => {
|
|
192
164
|
const inline = call('edit', { changes });
|
|
193
165
|
let upload = held;
|
|
194
|
-
if (upload === undefined && Buffer.byteLength(
|
|
166
|
+
if (upload === undefined && Buffer.byteLength(mcpBody(inline, operation)) > MCP_REQUEST_BYTES) {
|
|
195
167
|
const prepared = await staged(operation, changes);
|
|
196
168
|
if ('refused' in prepared)
|
|
197
169
|
return prepared;
|
|
@@ -251,13 +223,13 @@ export async function openBridge(house, copies, conversationId, signal) {
|
|
|
251
223
|
return {
|
|
252
224
|
env: {
|
|
253
225
|
HOUSE_BRIDGE: socketPath,
|
|
254
|
-
PATH:
|
|
226
|
+
PATH: path,
|
|
255
227
|
GIT_CONFIG_COUNT: '1',
|
|
256
228
|
GIT_CONFIG_KEY_0: `url.${proxy}/app/.insteadOf`,
|
|
257
229
|
GIT_CONFIG_VALUE_0: app,
|
|
258
230
|
},
|
|
259
231
|
tool: async (name, args) => {
|
|
260
|
-
const forwarded = await forward('/', JSON.stringify(
|
|
232
|
+
const forwarded = await forward('/', JSON.stringify(call(name, args)));
|
|
261
233
|
if (forwarded.status !== 200)
|
|
262
234
|
throw new Error(`House refused ${name} with ${forwarded.status}: ${forwarded.text}`);
|
|
263
235
|
const answer = JSON.parse(forwarded.text);
|
package/dist/clis.js
CHANGED
|
@@ -34,7 +34,7 @@ export const CLIS = {
|
|
|
34
34
|
bin: 'codex',
|
|
35
35
|
minimum: '0.159.1',
|
|
36
36
|
install: 'curl -fsSL https://chatgpt.com/codex/install.sh | sh',
|
|
37
|
-
adapter: { package: '@agentclientprotocol/codex-acp', bin: 'codex-acp', executable: 'CODEX_PATH' },
|
|
37
|
+
adapter: { package: '@agentclientprotocol/codex-acp', bin: 'codex-acp', executable: 'CODEX_PATH', env: {} },
|
|
38
38
|
args: [],
|
|
39
39
|
signedIn: { command: ['login', 'status'] },
|
|
40
40
|
login: { args: ['login', '--device-auth'], code: 'show' },
|
|
@@ -52,6 +52,7 @@ export const CLIS = {
|
|
|
52
52
|
package: '@agentclientprotocol/claude-agent-acp',
|
|
53
53
|
bin: 'claude-agent-acp',
|
|
54
54
|
executable: 'CLAUDE_CODE_EXECUTABLE',
|
|
55
|
+
env: { CLAUDE_CODE_ENTRYPOINT: 'claude-agent-acp' },
|
|
55
56
|
},
|
|
56
57
|
args: [],
|
|
57
58
|
signedIn: { command: ['auth', 'status'] },
|
|
@@ -153,10 +154,19 @@ function loginShell() {
|
|
|
153
154
|
}
|
|
154
155
|
const FOUND = 'house-kit-cli ';
|
|
155
156
|
const LOOKUP = `found=$(command -v "$1") || exit 0; case $found in /*) ;; *) found=$(pwd -P)/$found ;; esac; printf "${FOUND}%s\\n" "$found"`;
|
|
157
|
+
const USER_BIN = 'case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) PATH="$PATH:$HOME/.local/bin" ;; esac; export PATH';
|
|
158
|
+
function inLoginShell(script, argument = '') {
|
|
159
|
+
return run(loginShell(), ['-l', '-i', '-c', `exec /bin/sh -c '${USER_BIN}; ${script}' sh ${argument}`], READ_MS, true);
|
|
160
|
+
}
|
|
156
161
|
export async function locate(kind) {
|
|
157
|
-
const ran = await
|
|
162
|
+
const ran = await inLoginShell(LOOKUP, CLIS[kind].bin);
|
|
158
163
|
return ran.stdout.split('\n').findLast((line) => line.startsWith(FOUND))?.slice(FOUND.length) ?? null;
|
|
159
164
|
}
|
|
165
|
+
export async function loginPath() {
|
|
166
|
+
const marker = 'house-kit-path ';
|
|
167
|
+
const ran = await inLoginShell(`printf "${marker}%s\\n" "$PATH"`);
|
|
168
|
+
return ran.stdout.split('\n').findLast((line) => line.startsWith(marker)).slice(marker.length);
|
|
169
|
+
}
|
|
160
170
|
export async function versionOf(path) {
|
|
161
171
|
return VERSION.exec((await run(path, ['--version'], READ_MS)).stdout)?.[0] ?? null;
|
|
162
172
|
}
|
package/dist/home.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
const ENROLMENT = 'credential.json';
|
|
5
5
|
const CONFIGURATION = 'kit.json';
|
|
6
|
+
const OWN_AGENT = 'own-agent.json';
|
|
6
7
|
export function kitHome() {
|
|
7
8
|
return process.env.HOUSE_KIT_HOME ?? join(homedir(), '.house-kit');
|
|
8
9
|
}
|
|
@@ -35,6 +36,15 @@ export function readEnrolment() {
|
|
|
35
36
|
export function writeEnrolment(enrolment) {
|
|
36
37
|
return writeHome(ENROLMENT, enrolment);
|
|
37
38
|
}
|
|
39
|
+
export function readOwnAgent() {
|
|
40
|
+
return readHome(OWN_AGENT);
|
|
41
|
+
}
|
|
42
|
+
export function writeOwnAgent(ownAgent) {
|
|
43
|
+
return writeHome(OWN_AGENT, ownAgent);
|
|
44
|
+
}
|
|
45
|
+
export function forgetOwnAgent() {
|
|
46
|
+
return rm(join(kitHome(), OWN_AGENT), { force: true });
|
|
47
|
+
}
|
|
38
48
|
export async function readConfiguration() {
|
|
39
49
|
return { skills: (await readHome(CONFIGURATION))?.skills !== false };
|
|
40
50
|
}
|
package/dist/house-main.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createHash
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { request } from 'node:http';
|
|
4
|
+
import { request, setGlobalProxyFromEnv } from 'node:http';
|
|
5
5
|
import { basename, join } from 'node:path';
|
|
6
|
-
import { kitHome } from './home.js';
|
|
6
|
+
import { kitHome, readOwnAgent } from './home.js';
|
|
7
|
+
import { callHouse, rpc, UNREACHABLE } from './mcp.js';
|
|
7
8
|
import { refusalOf } from './refusals.js';
|
|
8
9
|
const USAGE = "usage: house <tool> ['<arguments as JSON>']";
|
|
9
10
|
const GIT_USAGE = 'usage: house git push [commit] [--owner]';
|
|
10
11
|
const ARGUMENTS = `the arguments are one JSON object in single quotes, like house search '{"query":"invoice"}'`;
|
|
11
12
|
const CLOSED = "this conversation's House connection is closed; nothing to do from here";
|
|
12
13
|
const REVOKED = 'this conversation no longer has House access; nothing to do from here';
|
|
14
|
+
const UNCONNECTED = "this computer's own agent has no House connection; run kit login";
|
|
15
|
+
const DISCONNECTED = "House no longer accepts this computer's own agent; run kit login";
|
|
13
16
|
const socketPath = process.env.HOUSE_BRIDGE;
|
|
14
17
|
function refused(path, status, text) {
|
|
15
18
|
const refusal = refusalOf(text);
|
|
@@ -42,8 +45,21 @@ function bridged(path, body, headers = { 'content-type': 'application/json' }, t
|
|
|
42
45
|
sent.end(body instanceof Buffer ? body : JSON.stringify(body));
|
|
43
46
|
});
|
|
44
47
|
}
|
|
48
|
+
async function direct(message) {
|
|
49
|
+
const ownAgent = await readOwnAgent();
|
|
50
|
+
if (ownAgent === null)
|
|
51
|
+
throw new Error(UNCONNECTED);
|
|
52
|
+
setGlobalProxyFromEnv();
|
|
53
|
+
const answer = await callHouse(ownAgent.house, ownAgent.credential, message).catch(() => {
|
|
54
|
+
throw new Error(UNREACHABLE);
|
|
55
|
+
});
|
|
56
|
+
if (answer.status === 200)
|
|
57
|
+
return answer.text;
|
|
58
|
+
throw new Error(answer.status === 401 ? DISCONNECTED : (refusalOf(answer.text)?.text ?? `House answered ${answer.status}`));
|
|
59
|
+
}
|
|
45
60
|
async function mcp(method, params) {
|
|
46
|
-
const
|
|
61
|
+
const message = rpc(method, params);
|
|
62
|
+
const answer = JSON.parse(socketPath === undefined ? await direct(message) : await bridged('/', message));
|
|
47
63
|
if (answer.error !== undefined)
|
|
48
64
|
throw new Error(answer.error.message);
|
|
49
65
|
return answer.result;
|
|
@@ -122,10 +138,10 @@ async function house([verb, argument]) {
|
|
|
122
138
|
return `${tool.description}\n${JSON.stringify(tool.inputSchema)}`;
|
|
123
139
|
}
|
|
124
140
|
const args = argumentsOf(argument);
|
|
125
|
-
if (verb === 'upload_attachment') {
|
|
141
|
+
if (socketPath !== undefined && verb === 'upload_attachment') {
|
|
126
142
|
return JSON.stringify({ attachment: await upload(args.path, args.room_ref, 'upload_attachment needs "path", a local file') });
|
|
127
143
|
}
|
|
128
|
-
if (verb === 'append_record' && Array.isArray(args.attachments)) {
|
|
144
|
+
if (socketPath !== undefined && verb === 'append_record' && Array.isArray(args.attachments)) {
|
|
129
145
|
const attachments = [];
|
|
130
146
|
for (const path of args.attachments) {
|
|
131
147
|
attachments.push(await upload(path, args.room_ref, 'each attachments entry is the path of a local file'));
|
|
@@ -139,15 +155,9 @@ async function house([verb, argument]) {
|
|
|
139
155
|
return text;
|
|
140
156
|
}
|
|
141
157
|
const argv = process.argv.slice(2);
|
|
142
|
-
|
|
143
|
-
process.
|
|
158
|
+
(argv[0] === 'git' ? gitPush(argv.slice(1)) : house(argv)).then((text) => {
|
|
159
|
+
process.stdout.write(`${text}\n`);
|
|
160
|
+
}, (error) => {
|
|
161
|
+
process.stderr.write(`house: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
144
162
|
process.exitCode = 1;
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
(argv[0] === 'git' ? gitPush(argv.slice(1)) : house(argv)).then((text) => {
|
|
148
|
-
process.stdout.write(`${text}\n`);
|
|
149
|
-
}, (error) => {
|
|
150
|
-
process.stderr.write(`house: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
151
|
-
process.exitCode = 1;
|
|
152
|
-
});
|
|
153
|
-
}
|
|
163
|
+
});
|
package/dist/login.js
CHANGED
|
@@ -4,7 +4,8 @@ import { hostname } from 'node:os';
|
|
|
4
4
|
import { createInterface } from 'node:readline/promises';
|
|
5
5
|
import { parseArgs } from 'node:util';
|
|
6
6
|
import { post } from './api.js';
|
|
7
|
-
import { readEnrolment, writeEnrolment } from './home.js';
|
|
7
|
+
import { forgetOwnAgent, readEnrolment, readOwnAgent, writeEnrolment, writeOwnAgent } from './home.js';
|
|
8
|
+
import { callHouse, rpc } from './mcp.js';
|
|
8
9
|
const DEFAULT_HOUSE = 'https://agents.house';
|
|
9
10
|
function listen() {
|
|
10
11
|
return new Promise((resolve, reject) => {
|
|
@@ -48,6 +49,14 @@ function listen() {
|
|
|
48
49
|
});
|
|
49
50
|
});
|
|
50
51
|
}
|
|
52
|
+
async function accepted(house, credential) {
|
|
53
|
+
const checked = await callHouse(house, credential, rpc('tools/list', {}));
|
|
54
|
+
if (checked.status === 200)
|
|
55
|
+
return true;
|
|
56
|
+
if (checked.status === 401)
|
|
57
|
+
return false;
|
|
58
|
+
throw new Error(`House answered ${checked.status} when Kit checked this computer's own agent's connection: ${checked.text}`);
|
|
59
|
+
}
|
|
51
60
|
async function typed(question) {
|
|
52
61
|
const reader = createInterface({ input: process.stdin, output: process.stdout });
|
|
53
62
|
const closed = new Promise((_, reject) => {
|
|
@@ -73,17 +82,27 @@ export async function login(argv) {
|
|
|
73
82
|
const house = values.house ?? enrolled?.house ?? DEFAULT_HOUSE;
|
|
74
83
|
const replaces = process.env.HOUSE_KIT_REPLACES;
|
|
75
84
|
const environment = replaces === undefined ? (values.environment ?? (enrolled?.house === house ? enrolled.environment : undefined)) : undefined;
|
|
85
|
+
const ownAgent = await readOwnAgent();
|
|
86
|
+
const held = ownAgent !== null && ownAgent.house === house && (await accepted(house, ownAgent.credential));
|
|
76
87
|
const verifier = randomBytes(32).toString('base64url');
|
|
77
88
|
const started = {
|
|
78
89
|
...(environment === undefined
|
|
79
90
|
? { act: 'new_environment', label: hostname(), ...(replaces === undefined ? {} : { replaces }) }
|
|
80
91
|
: { act: 'existing_environment', environment_id: environment }),
|
|
81
92
|
code_challenge: createHash('sha256').update(verifier).digest('base64url'),
|
|
93
|
+
...(held ? {} : { headless: true }),
|
|
82
94
|
};
|
|
83
95
|
const exchange = async (code) => {
|
|
84
96
|
const exchanged = await post(house, '/kit/token', { code, code_verifier: verifier });
|
|
85
97
|
await writeEnrolment({ house, environment: exchanged.environment, credential: exchanged.credential });
|
|
86
98
|
process.stdout.write(`Environment ${exchanged.environment} is connected to ${house}.\n`);
|
|
99
|
+
if (exchanged.headless_credential !== undefined) {
|
|
100
|
+
await writeOwnAgent({ house, user: exchanged.user, credential: exchanged.headless_credential });
|
|
101
|
+
}
|
|
102
|
+
else if (held && ownAgent.user !== exchanged.user) {
|
|
103
|
+
await forgetOwnAgent();
|
|
104
|
+
process.stdout.write("This computer's own agent was another User's; run kit login again to connect it to you.\n");
|
|
105
|
+
}
|
|
87
106
|
};
|
|
88
107
|
if (values.manual) {
|
|
89
108
|
const { start_url } = await post(house, '/kit', {
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { KIT_VERSION } from './clis.js';
|
|
3
|
+
const MCP_PROTOCOL_VERSION = '2026-07-28';
|
|
4
|
+
export const UNREACHABLE = 'House is unreachable; call again in a minute';
|
|
5
|
+
export function rpc(method, params) {
|
|
6
|
+
return { jsonrpc: '2.0', id: randomUUID(), method, params };
|
|
7
|
+
}
|
|
8
|
+
export function mcpBody(message, operation) {
|
|
9
|
+
return JSON.stringify({
|
|
10
|
+
...message,
|
|
11
|
+
params: {
|
|
12
|
+
...message.params,
|
|
13
|
+
_meta: {
|
|
14
|
+
'io.modelcontextprotocol/protocolVersion': MCP_PROTOCOL_VERSION,
|
|
15
|
+
'io.modelcontextprotocol/clientInfo': { name: '@agentshouse/kit', version: KIT_VERSION },
|
|
16
|
+
'io.modelcontextprotocol/clientCapabilities': {},
|
|
17
|
+
...(operation === null ? {} : { 'agents.house/agent-operation': operation }),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export async function callHouse(origin, credential, message, operation = null, signal) {
|
|
23
|
+
const tool = message.method === 'tools/call' ? String(message.params.name) : null;
|
|
24
|
+
const answer = await fetch(new URL('/', origin), {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
'content-type': 'application/json',
|
|
28
|
+
authorization: `Bearer ${credential}`,
|
|
29
|
+
accept: 'application/json, text/event-stream',
|
|
30
|
+
'mcp-protocol-version': MCP_PROTOCOL_VERSION,
|
|
31
|
+
'mcp-method': message.method,
|
|
32
|
+
...(tool === null ? {} : { 'mcp-name': tool }),
|
|
33
|
+
},
|
|
34
|
+
body: mcpBody(message, operation),
|
|
35
|
+
signal,
|
|
36
|
+
});
|
|
37
|
+
return { status: answer.status, text: await answer.text() };
|
|
38
|
+
}
|