@agentshouse/kit 0.2.1-cb0bd1cadcfed → 0.2.1-cbabe8ad337ff
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 +11 -11
- package/dist/acp.js +16 -93
- package/dist/agents.js +41 -27
- package/dist/api.js +3 -3
- package/dist/app-git.js +64 -0
- package/dist/bootstrap.json +4 -4
- package/dist/bridge.js +7 -22
- package/dist/clis.js +83 -70
- package/dist/codex-main.js +58 -0
- package/dist/conversations.js +279 -235
- package/dist/end.js +1 -1
- package/dist/events.js +521 -0
- package/dist/files.js +14 -0
- package/dist/house-main.js +28 -10
- package/dist/instructions.js +1 -1
- package/dist/kit-main.js +3 -0
- package/dist/login.js +23 -98
- package/dist/logout.js +16 -13
- package/dist/parts.js +64 -0
- package/dist/resident.js +12 -12
- package/dist/scope.js +203 -0
- package/dist/shell.js +3 -0
- package/dist/sign-in.js +34 -10
- package/dist/work.js +348 -0
- package/package.json +1 -1
- package/dist/blocks.js +0 -51
package/dist/clis.js
CHANGED
|
@@ -1,57 +1,60 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { constants, readFileSync } from 'node:fs';
|
|
3
|
+
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { homedir, userInfo } from 'node:os';
|
|
5
|
-
import { join } from 'node:path';
|
|
5
|
+
import { delimiter, isAbsolute, join } from 'node:path';
|
|
6
6
|
import { kitHome } from './home.js';
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const RAW_SUBTYPES = [
|
|
8
|
+
'background_tasks_changed',
|
|
9
|
+
'task_started',
|
|
10
|
+
'task_updated',
|
|
11
|
+
'task_notification',
|
|
12
|
+
'session_state_changed',
|
|
13
|
+
'hook_started',
|
|
14
|
+
'hook_response',
|
|
15
|
+
];
|
|
16
|
+
async function executable(path) {
|
|
17
|
+
try {
|
|
18
|
+
await access(path, constants.X_OK);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function allowHouseInCodex(path) {
|
|
26
|
+
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);
|
|
9
31
|
const rules = join(process.env.CODEX_HOME || join(homedir(), '.codex'), 'rules');
|
|
10
32
|
await mkdir(rules, { recursive: true });
|
|
11
|
-
await writeFile(join(rules, 'house.rules'),
|
|
33
|
+
await writeFile(join(rules, 'house.rules'), `prefix_rule(pattern = ["house"], decision = "allow")\nhost_executable(name = "house", paths = ${JSON.stringify(houses)})\n`);
|
|
12
34
|
}
|
|
13
35
|
async function nothing() { }
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
return undefined;
|
|
17
|
-
return params.update._meta?.codex?.threadStatus?.type;
|
|
18
|
-
}
|
|
19
|
-
function asyncTask({ method, params }) {
|
|
20
|
-
const update = params?.update;
|
|
21
|
-
if (method !== 'session/update' || update?.asyncTaskId === undefined)
|
|
22
|
-
return null;
|
|
23
|
-
if (update.sessionUpdate === 'async_task_spawned')
|
|
24
|
-
return { id: update.asyncTaskId, running: true };
|
|
25
|
-
if (update.sessionUpdate === 'async_task_state_update' && !RUNNING_JOB.has(update.state ?? '')) {
|
|
26
|
-
return { id: update.asyncTaskId, running: false };
|
|
27
|
-
}
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
function airPhase({ _meta }) {
|
|
31
|
-
const phase = _meta?.jetbrains?.air?.phase;
|
|
32
|
-
if (phase === 'commentary')
|
|
33
|
-
return 'note';
|
|
34
|
-
if (phase === 'final_answer')
|
|
35
|
-
return 'answer';
|
|
36
|
-
return null;
|
|
36
|
+
function bridgeInClaudeSandbox(bridge) {
|
|
37
|
+
return process.platform === 'darwin' ? { allowUnixSockets: [bridge] } : { allowAllUnixSockets: true };
|
|
37
38
|
}
|
|
38
39
|
export const CLIS = {
|
|
39
40
|
'codex-acp': {
|
|
40
41
|
bin: 'codex',
|
|
41
42
|
minimum: '0.159.1',
|
|
42
43
|
install: 'curl -fsSL https://chatgpt.com/codex/install.sh | sh',
|
|
43
|
-
adapter: {
|
|
44
|
+
adapter: {
|
|
45
|
+
package: '@agentclientprotocol/codex-acp',
|
|
46
|
+
bin: 'codex-acp',
|
|
47
|
+
executable: 'CODEX_PATH',
|
|
48
|
+
env: { CODEX_CONFIG: JSON.stringify({ features: { shell_zsh_fork: true } }) },
|
|
49
|
+
},
|
|
44
50
|
args: [],
|
|
45
51
|
fullAccess: 'agent-full-access',
|
|
46
|
-
sessionMeta: {},
|
|
52
|
+
sessionMeta: () => ({}),
|
|
53
|
+
renames: true,
|
|
47
54
|
allowHouse: allowHouseInCodex,
|
|
48
55
|
signedIn: { command: ['login', 'status'] },
|
|
49
56
|
login: { args: ['login', '--device-auth'], code: 'show' },
|
|
50
|
-
|
|
51
|
-
turnStarted: (notice) => threadStatus(notice) === 'active',
|
|
52
|
-
turnEnded: (notice) => threadStatus(notice) === 'idle',
|
|
53
|
-
job: asyncTask,
|
|
54
|
-
phase: airPhase,
|
|
57
|
+
air: ['asyncTasks', 'nativeSubagentSessions'],
|
|
55
58
|
},
|
|
56
59
|
'claude-agent-acp': {
|
|
57
60
|
bin: 'claude',
|
|
@@ -65,40 +68,36 @@ export const CLIS = {
|
|
|
65
68
|
},
|
|
66
69
|
args: [],
|
|
67
70
|
fullAccess: 'bypassPermissions',
|
|
68
|
-
sessionMeta:
|
|
71
|
+
sessionMeta: (bridge, title) => ({
|
|
72
|
+
claudeCode: {
|
|
73
|
+
options: {
|
|
74
|
+
allowedTools: ['Bash(house:*)'],
|
|
75
|
+
includeHookEvents: true,
|
|
76
|
+
title,
|
|
77
|
+
settings: { sandbox: { network: bridgeInClaudeSandbox(bridge) } },
|
|
78
|
+
},
|
|
79
|
+
emitRawSDKMessages: RAW_SUBTYPES.map((subtype) => ({ type: 'system', subtype })),
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
renames: false,
|
|
69
83
|
allowHouse: nothing,
|
|
70
84
|
signedIn: { command: ['auth', 'status'] },
|
|
71
85
|
login: { args: ['auth', 'login', '--claudeai'], code: 'collect', rejected: 'Invalid code' },
|
|
72
|
-
|
|
73
|
-
turnStarted: () => false,
|
|
74
|
-
turnEnded: ({ method, params }) => method === 'session/update' &&
|
|
75
|
-
params?.update?.sessionUpdate === 'usage_update' &&
|
|
76
|
-
params.update._meta?.['_claude/origin'] !== undefined,
|
|
77
|
-
job: asyncTask,
|
|
78
|
-
phase: () => null,
|
|
86
|
+
air: ['asyncTasks', 'sessionFailure'],
|
|
79
87
|
},
|
|
80
88
|
'grok-build': {
|
|
81
89
|
bin: 'grok',
|
|
82
90
|
minimum: '1.0.46',
|
|
83
91
|
install: 'curl -fsSL https://x.ai/cli/install.sh | bash',
|
|
84
92
|
adapter: null,
|
|
85
|
-
args: ['agent', '--always-approve', '--no-leader', 'stdio'],
|
|
93
|
+
args: ['--sandbox', 'off', 'agent', '--always-approve', '--no-leader', 'stdio'],
|
|
86
94
|
fullAccess: null,
|
|
87
|
-
sessionMeta: {},
|
|
95
|
+
sessionMeta: () => ({}),
|
|
96
|
+
renames: false,
|
|
88
97
|
allowHouse: nothing,
|
|
89
98
|
signedIn: { initializeMeta: 'defaultAuthMethodId' },
|
|
90
99
|
login: { args: ['login', '--device-auth'], code: 'show' },
|
|
91
|
-
|
|
92
|
-
turnStarted: ({ method, params }) => method === '_x.ai/queue/changed' && params?.runningPromptId !== undefined,
|
|
93
|
-
turnEnded: ({ method, params }) => method === '_x.ai/session_notification' && params?.update?.sessionUpdate === 'turn_completed',
|
|
94
|
-
job: ({ method, params }) => {
|
|
95
|
-
if (method === '_x.ai/task_backgrounded')
|
|
96
|
-
return { id: params.update.task_id, running: true };
|
|
97
|
-
if (method === '_x.ai/task_completed')
|
|
98
|
-
return { id: params.update.task_snapshot.task_id, running: false };
|
|
99
|
-
return null;
|
|
100
|
-
},
|
|
101
|
-
phase: () => null,
|
|
100
|
+
air: ['asyncTasks'],
|
|
102
101
|
},
|
|
103
102
|
};
|
|
104
103
|
const KIT_PACKAGE = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
@@ -135,8 +134,22 @@ export function run(command, args, timeoutMs, detached = false, signal) {
|
|
|
135
134
|
timeout: timeoutMs,
|
|
136
135
|
env: withoutProxy(),
|
|
137
136
|
detached,
|
|
138
|
-
signal,
|
|
137
|
+
signal: detached ? undefined : signal,
|
|
139
138
|
});
|
|
139
|
+
const stop = () => {
|
|
140
|
+
try {
|
|
141
|
+
process.kill(-child.pid, 'SIGKILL');
|
|
142
|
+
}
|
|
143
|
+
catch { }
|
|
144
|
+
};
|
|
145
|
+
if (detached && signal?.aborted)
|
|
146
|
+
stop();
|
|
147
|
+
if (detached)
|
|
148
|
+
signal?.addEventListener('abort', stop, { once: true });
|
|
149
|
+
const ended = (ran) => {
|
|
150
|
+
signal?.removeEventListener('abort', stop);
|
|
151
|
+
resolve(ran);
|
|
152
|
+
};
|
|
140
153
|
let stdout = '';
|
|
141
154
|
let output = '';
|
|
142
155
|
child.stdout.setEncoding('utf8').on('data', (chunk) => {
|
|
@@ -146,25 +159,25 @@ export function run(command, args, timeoutMs, detached = false, signal) {
|
|
|
146
159
|
child.stderr.setEncoding('utf8').on('data', (chunk) => {
|
|
147
160
|
output += chunk;
|
|
148
161
|
});
|
|
149
|
-
child.on('error', (error) =>
|
|
150
|
-
child.on('close', (status) =>
|
|
162
|
+
child.on('error', (error) => ended({ status: null, stdout, output: error.message }));
|
|
163
|
+
child.on('close', (status) => ended({ status, stdout, output }));
|
|
151
164
|
});
|
|
152
165
|
}
|
|
153
166
|
export function failureOf(ran, program) {
|
|
154
167
|
return ran.output.trim().slice(-CAUSE_CHARACTERS) || `${program} exited with ${ran.status ?? 'a signal'}`;
|
|
155
168
|
}
|
|
156
|
-
export async function installAdapter(kind) {
|
|
169
|
+
export async function installAdapter(kind, signal) {
|
|
157
170
|
const adapter = CLIS[kind].adapter;
|
|
158
171
|
if (adapter === null)
|
|
159
172
|
return null;
|
|
160
173
|
const release = KIT_PACKAGE.peerDependencies[adapter.package];
|
|
161
174
|
if ((await installedAdapter(kind)) === release)
|
|
162
175
|
return null;
|
|
163
|
-
const ran = await run('npm', ['install', '--prefix', prefix(kind), '--no-save', '--no-audit', '--no-fund', '--omit=optional', `${adapter.package}@${release}`], INSTALL_MS);
|
|
176
|
+
const ran = await run('npm', ['install', '--prefix', prefix(kind), '--no-save', '--no-audit', '--no-fund', '--omit=optional', `${adapter.package}@${release}`], INSTALL_MS, true, signal);
|
|
164
177
|
return ran.status === 0 ? null : failureOf(ran, 'npm');
|
|
165
178
|
}
|
|
166
|
-
export async function installCli(kind) {
|
|
167
|
-
const ran = await run('bash', ['-o', 'pipefail', '-c', CLIS[kind].install], INSTALL_MS, true);
|
|
179
|
+
export async function installCli(kind, signal) {
|
|
180
|
+
const ran = await run('bash', ['-o', 'pipefail', '-c', CLIS[kind].install], INSTALL_MS, true, signal);
|
|
168
181
|
return ran.status === 0 ? null : failureOf(ran, 'the install');
|
|
169
182
|
}
|
|
170
183
|
function loginShell() {
|
|
@@ -173,11 +186,11 @@ function loginShell() {
|
|
|
173
186
|
const FOUND = 'house-kit-cli ';
|
|
174
187
|
const LOOKUP = `found=$(command -v "$1") || exit 0; case $found in /*) ;; *) found=$(pwd -P)/$found ;; esac; printf "${FOUND}%s\\n" "$found"`;
|
|
175
188
|
const USER_BIN = 'case ":$PATH:" in *":$HOME/.local/bin:"*) ;; *) PATH="$PATH:$HOME/.local/bin" ;; esac; export PATH';
|
|
176
|
-
function inLoginShell(script, argument = '') {
|
|
177
|
-
return run(loginShell(), ['-l', '-i', '-c', `exec /bin/sh -c '${USER_BIN}; ${script}' sh ${argument}`], READ_MS, true);
|
|
189
|
+
function inLoginShell(script, argument = '', signal) {
|
|
190
|
+
return run(loginShell(), ['-l', '-i', '-c', `exec /bin/sh -c '${USER_BIN}; ${script}' sh ${argument}`], READ_MS, true, signal);
|
|
178
191
|
}
|
|
179
|
-
export async function locate(kind) {
|
|
180
|
-
const ran = await inLoginShell(LOOKUP, CLIS[kind].bin);
|
|
192
|
+
export async function locate(kind, signal) {
|
|
193
|
+
const ran = await inLoginShell(LOOKUP, CLIS[kind].bin, signal);
|
|
181
194
|
return ran.stdout.split('\n').findLast((line) => line.startsWith(FOUND))?.slice(FOUND.length) ?? null;
|
|
182
195
|
}
|
|
183
196
|
export async function loginPath() {
|
|
@@ -185,8 +198,8 @@ export async function loginPath() {
|
|
|
185
198
|
const ran = await inLoginShell(`printf "${marker}%s\\n" "$PATH"`);
|
|
186
199
|
return ran.stdout.split('\n').findLast((line) => line.startsWith(marker)).slice(marker.length);
|
|
187
200
|
}
|
|
188
|
-
export async function versionOf(path) {
|
|
189
|
-
return VERSION.exec((await run(path, ['--version'], READ_MS)).stdout)?.[0] ?? null;
|
|
201
|
+
export async function versionOf(path, signal) {
|
|
202
|
+
return VERSION.exec((await run(path, ['--version'], READ_MS, false, signal)).stdout)?.[0] ?? null;
|
|
190
203
|
}
|
|
191
204
|
function numbers(version) {
|
|
192
205
|
return version.split(/[.-]/, 3).map(Number);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { connect } from 'node:net';
|
|
4
|
+
const KIT_ANSWER = '"house-kit-';
|
|
5
|
+
const SYNC = 'house-kit/sync';
|
|
6
|
+
function lines(each) {
|
|
7
|
+
let buffer = '';
|
|
8
|
+
return (chunk) => {
|
|
9
|
+
buffer += chunk;
|
|
10
|
+
for (let index = buffer.indexOf('\n'); index >= 0; index = buffer.indexOf('\n')) {
|
|
11
|
+
each(buffer.slice(0, index + 1));
|
|
12
|
+
buffer = buffer.slice(index + 1);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function answersKit(line) {
|
|
17
|
+
if (!line.includes(KIT_ANSWER))
|
|
18
|
+
return false;
|
|
19
|
+
try {
|
|
20
|
+
const message = JSON.parse(line);
|
|
21
|
+
return message.method === undefined && typeof message.id === 'string' && message.id.startsWith(KIT_ANSWER.slice(1));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const args = process.argv.slice(2);
|
|
28
|
+
const serving = args[0] === 'app-server';
|
|
29
|
+
if (process.env.HOUSE_KIT_SANDBOX === '1' && serving)
|
|
30
|
+
args.push('--disable', 'memories');
|
|
31
|
+
const codex = spawn(process.env.HOUSE_KIT_CODEX, args, { stdio: ['pipe', 'pipe', 'inherit'] });
|
|
32
|
+
const stream = serving ? connect(process.env.HOUSE_KIT_CODEX_STREAM) : null;
|
|
33
|
+
stream?.on('error', () => undefined);
|
|
34
|
+
process.stdin.setEncoding('utf8').on('data', lines((line) => codex.stdin.write(line)));
|
|
35
|
+
process.stdin.on('end', () => codex.stdin.end());
|
|
36
|
+
stream?.setEncoding('utf8').on('data', lines((line) => {
|
|
37
|
+
const message = JSON.parse(line);
|
|
38
|
+
if (message.method === SYNC)
|
|
39
|
+
stream.write(`${JSON.stringify({ jsonrpc: '2.0', id: message.id, result: {} })}\n`);
|
|
40
|
+
else
|
|
41
|
+
codex.stdin.write(line);
|
|
42
|
+
}));
|
|
43
|
+
codex.stdout.setEncoding('utf8').on('data', lines((line) => {
|
|
44
|
+
stream?.write(line);
|
|
45
|
+
if (answersKit(line) || process.stdout.write(line))
|
|
46
|
+
return;
|
|
47
|
+
codex.stdout.pause();
|
|
48
|
+
process.stdout.once('drain', () => codex.stdout.resume());
|
|
49
|
+
}));
|
|
50
|
+
codex.stdin.on('error', () => undefined);
|
|
51
|
+
for (const signal of ['SIGTERM', 'SIGINT', 'SIGHUP'])
|
|
52
|
+
process.on(signal, () => codex.kill(signal));
|
|
53
|
+
codex.on('exit', (code, signal) => {
|
|
54
|
+
if (signal === null)
|
|
55
|
+
process.exit(code ?? 1);
|
|
56
|
+
process.removeAllListeners(signal);
|
|
57
|
+
process.kill(process.pid, signal);
|
|
58
|
+
});
|