@agentshouse/kit 0.2.1-c903d1429e34a → 0.2.1-c96543a4c8b15
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 +5 -5
- package/dist/bootstrap.json +4 -4
- package/dist/clis.js +33 -7
- package/dist/conversations.js +5 -5
- package/dist/house-main.js +74 -35
- package/dist/instructions.js +2 -2
- package/dist/kit-main.js +50 -8
- package/dist/login.js +20 -2
- package/dist/parts.js +34 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,11 +38,11 @@ reconnects an Environment you already have; `--rooms <ref>[,<ref>...]` gives it
|
|
|
38
38
|
only those Rooms and `--no-private` every Room but your Private Room;
|
|
39
39
|
`--code <code>` connects immediately without a link.
|
|
40
40
|
A login also connects your own agent: outside an Agent conversation, `house <tool>` calls
|
|
41
|
-
House as you and `git` clones and pushes your Apps at `<house>/app/` as you,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
agent's connection, so `house` and `git` no
|
|
45
|
-
`kit login` reconnects the same Environment.
|
|
41
|
+
House as you and `git` clones and pushes your Apps at `<house>/app/` as you, until you revoke
|
|
42
|
+
that connection on Connections. `house help` lists the Tools. Codex sessions started after a
|
|
43
|
+
login run a lone `house` command outside their sandbox. `kit logout` disconnects this
|
|
44
|
+
computer, stops House Kit and ends your own agent's connection, so `house` and `git` no
|
|
45
|
+
longer act as you here; a later `kit login` reconnects the same Environment.
|
|
46
46
|
|
|
47
47
|
`kit resident` keeps the Environment connected and, as it connects, copies each
|
|
48
48
|
Room you chose for it as a Git repository in the workspace's `local-copies`.
|
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-c96543a4c8b15",
|
|
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-c96543a4c8b15/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-c96543a4c8b15/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-c96543a4c8b15/connect-windows.ps1"
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}
|
package/dist/clis.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { constants, readFileSync } from 'node:fs';
|
|
3
|
-
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { access, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { homedir, userInfo } from 'node:os';
|
|
5
5
|
import { delimiter, isAbsolute, join } from 'node:path';
|
|
6
6
|
import { kitHome } from './home.js';
|
|
7
|
+
const HOUSE_EXECUTABLES = /^host_executable\(name = "house", paths = (\[.*\])\)$/m;
|
|
8
|
+
function houseRule(houses) {
|
|
9
|
+
return `prefix_rule(pattern = ["house"], decision = "allow")\nhost_executable(name = "house", paths = ${JSON.stringify(houses)})\n`;
|
|
10
|
+
}
|
|
7
11
|
const RAW_SUBTYPES = [
|
|
8
12
|
'background_tasks_changed',
|
|
9
13
|
'task_started',
|
|
@@ -22,15 +26,37 @@ async function executable(path) {
|
|
|
22
26
|
return false;
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
|
-
async function
|
|
29
|
+
async function listedHouses(file) {
|
|
30
|
+
let text;
|
|
31
|
+
try {
|
|
32
|
+
text = await readFile(file, 'utf8');
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error.code === 'ENOENT')
|
|
36
|
+
return [];
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const listed = JSON.parse(HOUSE_EXECUTABLES.exec(text)?.[1] ?? '[]');
|
|
41
|
+
return Array.isArray(listed) ? listed.filter((house) => typeof house === 'string') : [];
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function allowHouseInCodex(path) {
|
|
26
48
|
const candidates = [...new Set(path.split(delimiter).filter((directory) => isAbsolute(directory)))].map((directory) => join(directory, 'house'));
|
|
27
|
-
const houses = [];
|
|
28
|
-
for (const candidate of candidates)
|
|
29
|
-
if (await executable(candidate))
|
|
30
|
-
houses.push(candidate);
|
|
31
49
|
const rules = join(process.env.CODEX_HOME || join(homedir(), '.codex'), 'rules');
|
|
50
|
+
const file = join(rules, 'house.rules');
|
|
51
|
+
const houses = [];
|
|
52
|
+
for (const house of [...(await listedHouses(file)), ...candidates]) {
|
|
53
|
+
if (!houses.includes(house) && (await executable(house)))
|
|
54
|
+
houses.push(house);
|
|
55
|
+
}
|
|
32
56
|
await mkdir(rules, { recursive: true });
|
|
33
|
-
|
|
57
|
+
const temporary = `${file}.${process.pid}`;
|
|
58
|
+
await writeFile(temporary, houseRule(houses));
|
|
59
|
+
await rename(temporary, file);
|
|
34
60
|
}
|
|
35
61
|
async function nothing() { }
|
|
36
62
|
function bridgeInClaudeSandbox(bridge) {
|
package/dist/conversations.js
CHANGED
|
@@ -502,7 +502,7 @@ export class Conversations {
|
|
|
502
502
|
if (conversation.turn === turn)
|
|
503
503
|
conversation.turn = null;
|
|
504
504
|
const body = Promise.allSettled(turn.uploads).then(() => ({
|
|
505
|
-
parts: turn.parts.
|
|
505
|
+
parts: turn.parts.saved(),
|
|
506
506
|
context: turn.parts.context,
|
|
507
507
|
...failed,
|
|
508
508
|
}));
|
|
@@ -647,7 +647,7 @@ export class Conversations {
|
|
|
647
647
|
turn.parts.plan(event.entries);
|
|
648
648
|
return;
|
|
649
649
|
case 'image':
|
|
650
|
-
turn.uploads.push(this.image(conversation, turn, event.data, event.mediaType));
|
|
650
|
+
turn.uploads.push(this.image(conversation, turn, turn.parts.reserve(), event.data, event.mediaType));
|
|
651
651
|
this.kit.changed();
|
|
652
652
|
return;
|
|
653
653
|
}
|
|
@@ -667,7 +667,7 @@ export class Conversations {
|
|
|
667
667
|
else
|
|
668
668
|
conversation.turn.parts.start(part);
|
|
669
669
|
}
|
|
670
|
-
async image(conversation, turn, data, mediaType) {
|
|
670
|
+
async image(conversation, turn, reserved, data, mediaType) {
|
|
671
671
|
const bytes = Buffer.from(data, 'base64');
|
|
672
672
|
const name = `image-${++turn.images}.${mediaType.split('/')[1]}`;
|
|
673
673
|
try {
|
|
@@ -680,10 +680,10 @@ export class Conversations {
|
|
|
680
680
|
const saved = declared.upload === undefined ? declared : await uploadBytes(declared.upload, bytes);
|
|
681
681
|
if (saved.save.status !== 'saved')
|
|
682
682
|
throw new Error(saved.save.failure ?? saved.save.status);
|
|
683
|
-
turn.parts.
|
|
683
|
+
turn.parts.arrive({ type: 'image', file: saved.version, name, media_type: mediaType }, reserved);
|
|
684
684
|
}
|
|
685
685
|
catch (error) {
|
|
686
|
-
turn.parts.
|
|
686
|
+
turn.parts.arrive({ type: 'marker', marker: 'retry', text: `${name} was not saved: ${causeOf(error)}` }, reserved);
|
|
687
687
|
}
|
|
688
688
|
}
|
|
689
689
|
}
|
package/dist/house-main.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { request, setGlobalProxyFromEnv } from 'node:http';
|
|
5
|
-
import {
|
|
5
|
+
import { extname, join } from 'node:path';
|
|
6
6
|
import { HOUSE_REF } from './git.js';
|
|
7
7
|
import { kitHome, readEnrolment, readOwnAgent } from './home.js';
|
|
8
8
|
import { callHouse, rpc, UNREACHABLE } from './mcp.js';
|
|
9
9
|
import { refusalOf } from './refusals.js';
|
|
10
10
|
import { relayed } from './relay.js';
|
|
11
|
-
import { parse } from 'yaml';
|
|
11
|
+
import { parse, stringify } from 'yaml';
|
|
12
12
|
const USAGE = "usage: house <tool> ['<arguments as JSON>']";
|
|
13
13
|
const PUSH_USAGE = 'usage: house push [commit] [--owner]';
|
|
14
14
|
const PUSH = "push [commit]: sends the Local copy's committed changes to House";
|
|
@@ -21,6 +21,33 @@ const REVOKED = 'this conversation no longer has House access; nothing to do fro
|
|
|
21
21
|
const UNCONNECTED = "this computer's own agent has no House connection; run kit login";
|
|
22
22
|
const DISCONNECTED = "House no longer accepts this computer's own agent; run kit login";
|
|
23
23
|
const socketPath = process.env.HOUSE_BRIDGE;
|
|
24
|
+
const MEDIA_TYPES = new Map([
|
|
25
|
+
['avif', 'image/avif'],
|
|
26
|
+
['csv', 'text/csv'],
|
|
27
|
+
['docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
|
|
28
|
+
['gif', 'image/gif'],
|
|
29
|
+
['heic', 'image/heic'],
|
|
30
|
+
['html', 'text/html'],
|
|
31
|
+
['jpeg', 'image/jpeg'],
|
|
32
|
+
['jpg', 'image/jpeg'],
|
|
33
|
+
['json', 'application/json'],
|
|
34
|
+
['m4a', 'audio/mp4'],
|
|
35
|
+
['md', 'text/markdown'],
|
|
36
|
+
['mov', 'video/quicktime'],
|
|
37
|
+
['mp3', 'audio/mpeg'],
|
|
38
|
+
['mp4', 'video/mp4'],
|
|
39
|
+
['ogg', 'audio/ogg'],
|
|
40
|
+
['pdf', 'application/pdf'],
|
|
41
|
+
['png', 'image/png'],
|
|
42
|
+
['pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
|
|
43
|
+
['svg', 'image/svg+xml'],
|
|
44
|
+
['txt', 'text/plain'],
|
|
45
|
+
['wav', 'audio/wav'],
|
|
46
|
+
['webm', 'video/webm'],
|
|
47
|
+
['webp', 'image/webp'],
|
|
48
|
+
['xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
|
|
49
|
+
['zip', 'application/zip'],
|
|
50
|
+
]);
|
|
24
51
|
function refused(path, status, text) {
|
|
25
52
|
const refusal = refusalOf(text);
|
|
26
53
|
if (refusal !== null)
|
|
@@ -98,28 +125,43 @@ async function sent(url, bytes, operation) {
|
|
|
98
125
|
return text;
|
|
99
126
|
throw new Error(refused(url, answer.status, text));
|
|
100
127
|
}
|
|
101
|
-
async function upload(
|
|
102
|
-
if (typeof
|
|
103
|
-
throw new Error(
|
|
104
|
-
const bytes = await localFile(
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
async function upload({ file, ...destination }) {
|
|
129
|
+
if (typeof file !== 'string' || file === '')
|
|
130
|
+
throw new Error('upload needs "file", a local file');
|
|
131
|
+
const bytes = await localFile(file);
|
|
132
|
+
const mediaType = MEDIA_TYPES.get(extname(file).slice(1).toLowerCase());
|
|
133
|
+
const answer = await called('upload', {
|
|
134
|
+
...destination,
|
|
108
135
|
bytes: bytes.length,
|
|
109
136
|
sha256: createHash('sha256').update(bytes).digest('hex'),
|
|
110
|
-
|
|
137
|
+
...(mediaType === undefined ? {} : { media_type: mediaType }),
|
|
138
|
+
});
|
|
139
|
+
const declared = parse(answer);
|
|
111
140
|
if (declared.upload === undefined)
|
|
112
|
-
return
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
141
|
+
return answer;
|
|
142
|
+
const written = JSON.parse(await sent(declared.upload.url, bytes, declared.upload.operation));
|
|
143
|
+
return stringify(written, { lineWidth: 0 }).trimEnd();
|
|
144
|
+
}
|
|
145
|
+
function unexpected(argument, usage) {
|
|
146
|
+
return new Error(`unexpected argument ${argument}; ${usage}`);
|
|
147
|
+
}
|
|
148
|
+
function accepted(args, flags, positionals, usage) {
|
|
149
|
+
const given = { flags: new Set(), positionals: [] };
|
|
150
|
+
for (const arg of args) {
|
|
151
|
+
if (flags.includes(arg))
|
|
152
|
+
given.flags.add(arg);
|
|
153
|
+
else if (!arg.startsWith('-') && given.positionals.length < positionals)
|
|
154
|
+
given.positionals.push(arg);
|
|
155
|
+
else
|
|
156
|
+
throw unexpected(arg, usage);
|
|
157
|
+
}
|
|
158
|
+
return given;
|
|
117
159
|
}
|
|
118
160
|
async function push(args) {
|
|
119
|
-
|
|
161
|
+
const { flags, positionals: [commit = 'HEAD'] } = accepted(args, ['--help', '--owner'], 1, PUSH_USAGE);
|
|
162
|
+
if (flags.has('--help'))
|
|
120
163
|
return PUSH_USAGE;
|
|
121
|
-
const owner =
|
|
122
|
-
const commit = args.find((arg) => arg !== '--owner') ?? 'HEAD';
|
|
164
|
+
const owner = flags.has('--owner');
|
|
123
165
|
if (owner && socketPath !== undefined)
|
|
124
166
|
throw new Error('--owner is not for Agent conversations; run house push without it');
|
|
125
167
|
if (!owner && socketPath === undefined)
|
|
@@ -130,7 +172,7 @@ async function push(args) {
|
|
|
130
172
|
return pushed.text;
|
|
131
173
|
}
|
|
132
174
|
async function fetched(args) {
|
|
133
|
-
if (args.
|
|
175
|
+
if (accepted(args, ['--help'], 0, FETCH_USAGE).flags.has('--help'))
|
|
134
176
|
return FETCH_USAGE;
|
|
135
177
|
const answer = JSON.parse(await bridged('/git/fetch', {}, undefined, socketPath ?? join(kitHome(), 'owner.sock')));
|
|
136
178
|
if (answer.refused)
|
|
@@ -151,28 +193,23 @@ function argumentsOf(argument) {
|
|
|
151
193
|
throw new Error(ARGUMENTS);
|
|
152
194
|
return parsed;
|
|
153
195
|
}
|
|
154
|
-
async function house(
|
|
155
|
-
|
|
196
|
+
async function house(args) {
|
|
197
|
+
const { flags, positionals: [verb, argument] } = accepted(args, ['--help'], 2, USAGE);
|
|
198
|
+
if (verb === 'help' && argument !== undefined)
|
|
199
|
+
throw unexpected(argument, USAGE);
|
|
200
|
+
if (flags.has('--help') && argument !== undefined)
|
|
201
|
+
throw unexpected('--help', USAGE);
|
|
202
|
+
if (verb === undefined || verb === 'help') {
|
|
156
203
|
return [USAGE, 'help', PUSH, FETCH, ...(await tools()).map((tool) => (tool.description === undefined ? tool.name : `${tool.name}: ${tool.description}`))].join('\n');
|
|
157
204
|
}
|
|
158
|
-
if (
|
|
205
|
+
if (flags.has('--help')) {
|
|
159
206
|
const tool = (await tools()).find((listed) => listed.name === verb);
|
|
160
207
|
if (tool === undefined)
|
|
161
208
|
throw new Error(`${verb} is no Tool; house find_command '{"query":"${verb}"}' finds Commands`);
|
|
162
209
|
return [...(tool.description === undefined ? [] : [tool.description]), JSON.stringify(tool.inputSchema)].join('\n');
|
|
163
210
|
}
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
return JSON.stringify({ attachment: await upload(args.path, args.room_ref, 'upload needs "path", a local file') });
|
|
167
|
-
}
|
|
168
|
-
if (verb === 'append_record' && Array.isArray(args.attachments)) {
|
|
169
|
-
const attachments = [];
|
|
170
|
-
for (const path of args.attachments) {
|
|
171
|
-
attachments.push(await upload(path, args.room_ref, 'each attachments entry is the path of a local file'));
|
|
172
|
-
}
|
|
173
|
-
args.attachments = attachments;
|
|
174
|
-
}
|
|
175
|
-
return called(verb, args);
|
|
211
|
+
const parsed = argumentsOf(argument);
|
|
212
|
+
return verb === 'upload' ? upload(parsed) : called(verb, parsed);
|
|
176
213
|
}
|
|
177
214
|
async function called(name, args) {
|
|
178
215
|
const result = await mcp('tools/call', { name, arguments: args });
|
|
@@ -182,7 +219,9 @@ async function called(name, args) {
|
|
|
182
219
|
return text;
|
|
183
220
|
}
|
|
184
221
|
const argv = process.argv.slice(2);
|
|
185
|
-
|
|
222
|
+
const named = argv.findIndex((arg) => !arg.startsWith('-'));
|
|
223
|
+
const rest = argv.filter((_arg, index) => index !== named);
|
|
224
|
+
(argv[named] === 'push' ? push(rest) : argv[named] === 'fetch' ? fetched(rest) : house(argv)).then((text) => {
|
|
186
225
|
process.stdout.write(`${text}\n`);
|
|
187
226
|
if (argv[0] === 'shell')
|
|
188
227
|
process.exitCode = Number(/^exit: (\d+)(?:\n|$)/.exec(text)?.[1] ?? 0);
|
package/dist/instructions.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { HOUSE_REF } from './git.js';
|
|
2
2
|
import { HOW_WE_WORK } from './skills.js';
|
|
3
3
|
const SOURCE_LINE = "From House:";
|
|
4
|
-
const HOUSE_LINE =
|
|
5
|
-
const FILE_LINE = 'To give the User a file
|
|
4
|
+
const HOUSE_LINE = "Work with House through the `house` CLI: run `house --help`, and `house find_command` for House's guides and formats.";
|
|
5
|
+
const FILE_LINE = 'To give the User a file, upload it with `house upload` and link it; for an App, a Site or a website, follow `house find_command \'{"query":"app"}\'`. Never start a server.';
|
|
6
6
|
const READ_LINE = "Optional per-Room instructions: `/rooms/private/rooms/<handle>/AGENTS.md`.";
|
|
7
7
|
const COPIES_LINE = `House Local copies; before work run \`house fetch\` and rebase onto \`${HOUSE_REF}\`; after it commit, then \`house push\`:`;
|
|
8
8
|
const UNREADABLE = /^(?:\S+: )?(?:path_not_found|room_not_found|operation_denied)\b/;
|
package/dist/kit-main.js
CHANGED
|
@@ -1,22 +1,64 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { setGlobalProxyFromEnv } from 'node:http';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
3
4
|
import { answerGitCredential } from './app-git.js';
|
|
4
5
|
import { login } from './login.js';
|
|
5
6
|
import { logout } from './logout.js';
|
|
6
7
|
import { resident } from './resident.js';
|
|
8
|
+
const USAGE = 'usage: kit <login|logout|resident>\n';
|
|
9
|
+
const COMMANDS = new Map([
|
|
10
|
+
[
|
|
11
|
+
'logout',
|
|
12
|
+
{
|
|
13
|
+
usage: 'usage: kit logout',
|
|
14
|
+
about: "disconnects this computer, stops House Kit and ends your own agent's connection",
|
|
15
|
+
operands: 0,
|
|
16
|
+
run: logout,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
[
|
|
20
|
+
'resident',
|
|
21
|
+
{
|
|
22
|
+
usage: 'usage: kit resident',
|
|
23
|
+
about: "keeps this Environment connected; House Kit's service runs it",
|
|
24
|
+
operands: 0,
|
|
25
|
+
run: resident,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
[
|
|
29
|
+
'git-credential',
|
|
30
|
+
{
|
|
31
|
+
usage: 'usage: kit git-credential <get|store|erase>',
|
|
32
|
+
about: "answers git with your own agent's House credential; git runs it",
|
|
33
|
+
operands: 1,
|
|
34
|
+
run: ([operation]) => answerGitCredential(operation),
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
]);
|
|
7
38
|
const [command, ...argv] = process.argv.slice(2);
|
|
8
39
|
setGlobalProxyFromEnv();
|
|
9
40
|
async function run() {
|
|
10
41
|
if (command === 'login')
|
|
11
42
|
return login(argv);
|
|
12
|
-
if (command === '
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
43
|
+
if (command === '--help') {
|
|
44
|
+
parseArgs({ args: argv });
|
|
45
|
+
process.stdout.write(USAGE);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const chosen = COMMANDS.get(command ?? '');
|
|
49
|
+
if (chosen === undefined) {
|
|
50
|
+
process.stderr.write(USAGE);
|
|
51
|
+
process.exitCode = 2;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const { values, positionals } = parseArgs({ args: argv, options: { help: { type: 'boolean', default: false } }, allowPositionals: true });
|
|
55
|
+
if (positionals.length > chosen.operands)
|
|
56
|
+
throw new Error(`unexpected argument ${positionals[chosen.operands]}; ${chosen.usage}`);
|
|
57
|
+
if (values.help) {
|
|
58
|
+
process.stdout.write(`${chosen.usage}\n ${chosen.about}\n`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
return chosen.run(positionals);
|
|
20
62
|
}
|
|
21
63
|
run().catch((error) => {
|
|
22
64
|
process.stderr.write(`kit: ${error instanceof Error ? error.message : String(error)}\n`);
|
package/dist/login.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createHash, randomBytes } from 'node:crypto';
|
|
3
3
|
import { hostname } from 'node:os';
|
|
4
|
+
import { delimiter, dirname } from 'node:path';
|
|
4
5
|
import { parseArgs } from 'node:util';
|
|
5
6
|
import { post, retrying } from './api.js';
|
|
6
7
|
import { placeAppGit } from './app-git.js';
|
|
8
|
+
import { allowHouseInCodex, loginPath } from './clis.js';
|
|
7
9
|
import { forgetOwnAgent, readDisconnected, readEnrolment, readOwnAgent, writeEnrolment, writeOwnAgent } from './home.js';
|
|
8
10
|
import { callHouse, rpc } from './mcp.js';
|
|
9
11
|
import { selectRelay } from './relay.js';
|
|
10
12
|
import { startService } from './service.js';
|
|
13
|
+
import { placeSkillSet } from './skills.js';
|
|
11
14
|
const DEFAULT_HOUSE = 'https://agents.house';
|
|
12
15
|
const OPENER = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
13
16
|
// Polling every two seconds brings the User's confirmation to the waiting Kit moments after they give it.
|
|
@@ -25,7 +28,8 @@ async function accepted(house, credential) {
|
|
|
25
28
|
return false;
|
|
26
29
|
throw new Error(`House answered ${checked.status} when Kit checked this computer's own agent's connection: ${checked.text}`);
|
|
27
30
|
}
|
|
28
|
-
const USAGE = `usage: kit login [--house <origin>] [--environment <id>] [--rooms <ref>[,<ref>...] | --no-private]
|
|
31
|
+
const USAGE = `usage: kit login [--house <origin>] [--environment <id>] [--rooms <ref>[,<ref>...] | --no-private]
|
|
32
|
+
kit login [--house <origin>] --code <code>
|
|
29
33
|
--house <origin> the House to connect to, ${DEFAULT_HOUSE} unless one is stored
|
|
30
34
|
--environment <id> reconnect this Environment
|
|
31
35
|
--rooms <ref>[,<ref>...] give this Environment only these Rooms
|
|
@@ -81,8 +85,9 @@ export async function login(argv) {
|
|
|
81
85
|
held = ownAgent !== null && ownAgent.house === house && (await accepted(house, ownAgent.credential));
|
|
82
86
|
const verifier = randomBytes(32).toString('base64url');
|
|
83
87
|
const started = await retrying(() => post(house, '/kit', {
|
|
88
|
+
label: hostname(),
|
|
84
89
|
...(environment === undefined
|
|
85
|
-
? { act: 'new_environment',
|
|
90
|
+
? { act: 'new_environment', ...(replaces === undefined ? {} : { replaces }) }
|
|
86
91
|
: { act: 'existing_environment', environment_id: environment }),
|
|
87
92
|
...rooms,
|
|
88
93
|
code_challenge: createHash('sha256').update(verifier).digest('base64url'),
|
|
@@ -92,6 +97,19 @@ export async function login(argv) {
|
|
|
92
97
|
open(started.start_url);
|
|
93
98
|
exchanged = await retrying(() => post(house, '/kit/token', { login: started.login, code_verifier: verifier }), undefined, () => POLL_INTERVAL_MS);
|
|
94
99
|
}
|
|
100
|
+
try {
|
|
101
|
+
await placeSkillSet();
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
process.stderr.write(`kit: the skills could not be placed: ${error.message}\n`);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const shell = await loginPath().catch(() => '');
|
|
108
|
+
await allowHouseInCodex([dirname(process.execPath), process.env.PATH ?? '', shell].join(delimiter));
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
process.stderr.write(`kit: Codex's rule for house could not be written: ${error.message}\n`);
|
|
112
|
+
}
|
|
95
113
|
await writeEnrolment({
|
|
96
114
|
house,
|
|
97
115
|
...(relay === undefined ? {} : { relay }),
|
package/dist/parts.js
CHANGED
|
@@ -2,7 +2,10 @@ export class Parts {
|
|
|
2
2
|
parts = [];
|
|
3
3
|
context = null;
|
|
4
4
|
emit;
|
|
5
|
+
made = [];
|
|
6
|
+
events = 0;
|
|
5
7
|
writing = null;
|
|
8
|
+
counting = null;
|
|
6
9
|
planned = null;
|
|
7
10
|
constructor(emit) {
|
|
8
11
|
this.emit = emit;
|
|
@@ -10,32 +13,34 @@ export class Parts {
|
|
|
10
13
|
write(type, id, text) {
|
|
11
14
|
if (text === '')
|
|
12
15
|
return;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
part.text += text;
|
|
17
|
-
this.emit({ op: 'append', index: last, text });
|
|
16
|
+
if (this.writing?.type === type && this.writing.id === id) {
|
|
17
|
+
this.parts[this.writing.index].text += text;
|
|
18
|
+
this.emit({ op: 'append', index: this.writing.index, text });
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
20
|
-
this.start({ type, text });
|
|
21
|
-
this.writing = { type, id };
|
|
21
|
+
this.writing = { type, id, index: this.start({ type, text }) };
|
|
22
22
|
}
|
|
23
23
|
command() {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (part?.type !== 'commands') {
|
|
27
|
-
this.start({ type: 'commands', count: 1 });
|
|
24
|
+
if (this.counting === null) {
|
|
25
|
+
this.counting = this.start({ type: 'commands', count: 1 });
|
|
28
26
|
return;
|
|
29
27
|
}
|
|
28
|
+
const part = this.parts[this.counting];
|
|
30
29
|
part.count++;
|
|
31
|
-
this.emit({ op: 'count', index:
|
|
30
|
+
this.emit({ op: 'count', index: this.counting, count: part.count });
|
|
32
31
|
}
|
|
33
32
|
start(part) {
|
|
34
33
|
this.writing = null;
|
|
35
|
-
this.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
this.counting = null;
|
|
35
|
+
return this.add(part, ++this.events);
|
|
36
|
+
}
|
|
37
|
+
reserve() {
|
|
38
|
+
this.writing = null;
|
|
39
|
+
this.counting = null;
|
|
40
|
+
return ++this.events;
|
|
41
|
+
}
|
|
42
|
+
arrive(part, reserved) {
|
|
43
|
+
this.add(part, reserved);
|
|
39
44
|
}
|
|
40
45
|
done(index, result) {
|
|
41
46
|
const part = this.parts[index];
|
|
@@ -55,10 +60,23 @@ export class Parts {
|
|
|
55
60
|
this.context = context;
|
|
56
61
|
this.emit({ op: 'context', ...context });
|
|
57
62
|
}
|
|
63
|
+
saved() {
|
|
64
|
+
return this.parts
|
|
65
|
+
.map((part, index) => ({ part, made: this.made[index] }))
|
|
66
|
+
.sort((a, b) => a.made - b.made)
|
|
67
|
+
.map(({ part }) => part);
|
|
68
|
+
}
|
|
58
69
|
operations() {
|
|
59
70
|
return [
|
|
60
71
|
...this.parts.map((part, index) => ({ op: 'start', index, part })),
|
|
61
72
|
...(this.context === null ? [] : [{ op: 'context', ...this.context }]),
|
|
62
73
|
];
|
|
63
74
|
}
|
|
75
|
+
add(part, made) {
|
|
76
|
+
this.parts.push(part);
|
|
77
|
+
this.made.push(made);
|
|
78
|
+
const index = this.parts.length - 1;
|
|
79
|
+
this.emit({ op: 'start', index, part });
|
|
80
|
+
return index;
|
|
81
|
+
}
|
|
64
82
|
}
|